Jos*_*shF 2 vb.net multithreading class process event-handling
我遇到了一个问题,即使我看到事件发生了,我的主表单也没有更新.让我解释一下这种情况并分享我的一些代码,因为我是业余爱好者,我肯定会很恐怖.
我创建了一个类来接受在后台运行进程的设置.我在该类中添加了一些自定义事件,因此我可以在我的表单中使用它而不是计时器.
我在那两个事件上休息了处理这些事件,我看到它们在安装开始后立即启动.
我查看数据并且它正在发生并且没有抛出任何异常.
起初我以为是因为datagridview有一些延迟问题.我通过我发现的一些技巧将其设置为双缓冲,但这并不重要.在数据网格中出现数据之前,仍有大约10秒的延迟.
我考虑过它并决定我真的不需要datagridview并用多行文本框替换控件,但它并没有什么区别.显示表单/文本框的更新仍需要10秒或更长时间.
我在下面提供了一些代码.
Public Shared WithEvents np As NewProcess
Run Code Online (Sandbox Code Playgroud)
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
np = New NewProcess
AddHandler np.InstallFinished, AddressOf np_InstallFinished
AddHandler np.InstallStarted, AddressOf np_InstallStarted
Catch ex As Exception
End Try
End Sub
Run Code Online (Sandbox Code Playgroud)
Protected Sub np_InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
InstallInProcess = False
If Not Description = Nothing Then
If Not ExitCode = Nothing Then
AddLog(String.Format("Completed install of {0} ({1}).", Description, ExitCode))
Else
AddLog(String.Format("Completed install of {0}.", Description))
End If
End If
RefreshButtons()
UpdateListofApps()
np.Dispose()
End Sub
Run Code Online (Sandbox Code Playgroud)
Protected Sub np_InstallStarted(ByVal Description As String)
InstallInProcess = True
If Not Description = Nothing Then AddLog(String.Format("Started the install of {0}.", Description))
End Sub
Run Code Online (Sandbox Code Playgroud)
Public Class NewProcess
Dim ProcessName As String
Dim ProcessVisibile As Boolean
Dim Arguments As String
Dim WaitforExit As Boolean
Dim Description As String
Dim ShellExecute As Boolean
Dim EC As Integer = Nothing 'Exit Code
Private IsBusy As Boolean = Nothing
Dim th As Threading.Thread
Public Event InstallFinished(ByVal Description As String, ByVal ExitCode As Integer)
Public Event InstallStarted(ByVal Description As String)
Public Function Busy() As Boolean
If IsBusy = Nothing Then Return False
Return IsBusy
End Function
Public Function ExitCode() As Integer
Return EC
End Function
Public Function ProcessDescription() As String
Return Description
End Function
''' <summary>
''' Starts a new multithreaded process.
''' </summary>
''' <param name="path">Path of the File to run</param>
''' <param name="Visible">Should application be visible?</param>
''' <param name="Arg">Arguments</param>
''' <param name="WaitforExit">Wait for application to exit?</param>
''' <param name="Description">Description that will show up in logs</param>
''' <remarks>Starts a new multithreaded process.</remarks>
Public Sub StartProcess(ByVal path As String, ByVal Visible As Boolean, Optional ByVal Arg As String = Nothing, Optional ByVal WaitforExit As Boolean = False, Optional ByVal Description As String = Nothing)
Try
Me.ProcessName = path
Me.ProcessVisibile = Visible
If Arguments = Nothing Then Me.Arguments = Arg
Me.Description = Description
Me.WaitforExit = WaitforExit
If IsBusy And WaitforExit Then
MessageBox.Show("Another install is already in process, please wait for previous install to finish.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Exit Sub
End If
If Not fn_FileExists(ProcessName) Then
MessageBox.Show("Could not find file " & ProcessName & ".", "Could not start process because file is missing.", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End If
th = New Threading.Thread(AddressOf NewThread)
With th
.IsBackground = True
If Not Description Is Nothing Then .Name = Description
.Start()
End With
Catch ex As Exception
End Try
End Sub
Private Sub NewThread()
Dim p As Process
Try
p = New Process
With p
.EnableRaisingEvents = True
.StartInfo.Arguments = Arguments
.StartInfo.FileName = ProcessName
.StartInfo.CreateNoWindow = ProcessVisibile
End With
If ProcessVisibile Then
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal
Else
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
End If
p.Start()
IsBusy = True
RaiseEvent InstallStarted(Description)
If WaitforExit Then
Do While p.HasExited = False
Threading.Thread.Sleep(500)
Loop
IsBusy = False
RaiseEvent InstallFinished(Description, p.ExitCode)
End If
EC = p.ExitCode
Catch ex As Exception
End Try
End Sub
Public Sub Dispose()
ProcessName = Nothing
ProcessVisibile = Nothing
Arguments = Nothing
WaitforExit = Nothing
Description = Nothing
EC = Nothing
InstallInProcess = Nothing
th.Join()
MemoryManagement.FlushMemory()
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
Sub AddLog(ByVal s As String)
Try
s = String.Format("[{0}] {1}", TimeOfDay.ToShortTimeString, s)
Form1.tbLogs.AppendText(s & vbCrLf)
Using st As New StreamWriter(LogFilePath, True)
st.WriteLine(s)
st.Flush()
End Using
Catch ex As Exception
End Try
End Sub
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?我完全失去了.
我已经尝试添加application.doevents,me.refresh和其他一些东西:(
Form1.tbLogs.AppendText(s & vbCrLf)
Run Code Online (Sandbox Code Playgroud)
标准的VB.NET陷阱.Form1是类名,而不是对表单的引用.不幸的是,VB.NET在VB6中实现了一个不合时宜的地方,这是合法的.但是当你使用线程时它会崩溃.您将获得另一个自动创建的表单对象,一个不可见的表单对象,因为它的Show()方法从未被调用过.否则作为一个doornail死了,因为线程没有抽一个消息循环.
您需要将对用户正在查看的实际表单对象的引用传递给worker类.Form1代码中Me的值.您还必须使用Control.Invoke,因为从另一个线程更新控件是不合法的.我建议您触发一个事件,而不是Form1可以订阅的事件,这样您的工作者类就不会感染UI的实现细节.