更新时间:2023-02-13 14:39:30
Form1.tbLogs.AppendText(s& vbCrLf)
/ pre>标准VB.NET陷阱Form1是一个类名,而不是对表单的引用不幸的是,VB.NET从VB6中实现了一个时间错误当你使用线程时,它会崩溃,你会得到另一个自动创建的表单对象,因为它的Show()方法从未被调用而不可见。线程不是一个消息循环。
你需要传递一个引用到用户正在查看的工作类的实际表单对象。我在Form1代码中,你还必须使用Control.Invoke,因为它是不合法的更新控件从另一个线程。我建议你启动一个事件,而Form1可以订阅,所以你的工作类是' t感染UI的实现细节。
I'm having an issue where my main form isn't updating even though I see the event fire off. Let me explain the situation and share some of my code which I'm sure will be horrible since I'm an amateur.
I created a class to take in the settings for running a process in the background. I add some custom events in that class so I could use that in my form instead of a timer.
I put a break on the two subs for that handle those events and I see them get kicked off as soon as an install starts.
I look at the data and it's coming across and no exceptions are thrown.
At first I thought it was because the datagridview had some latency issues. I set that to be double buffered through some tricks I found but it didn't matter. There was still a roughly 10 second delay before the data showed up in the datagrid.
I thought about it and decided I really didn't need a datagridview and replaced the control with a multiline textbox, but it didn't make a difference. It's still taking 10 seconds or longer to show updates to the form/textbox.
I've included some of my code below.
Public Shared WithEvents np As NewProcess
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
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
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
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
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
Any idea's? I'm at a complete loss.
I've tried adding application.doevents, me.refresh and quite a few other things :(
Form1.tbLogs.AppendText(s & vbCrLf)
Standard VB.NET trap. Form1 is a class name, not a reference to the form. Unfortunately, VB.NET implemented an anachronism from VB6 where that was legal. It however falls apart when you use threads. You'll get another form object automatically created, one that isn't visible because its Show() method was never called. Otherwise dead as a doornail since the thread is not pumping a message loop.
You'll need to pass a reference to the actual form object that the user is looking at to the worker class. The value of Me in the Form1 code. You will also have to use Control.Invoke since it isn't legal to update controls from another thread. I recommend you fire an event instead, one that Form1 can subscribe to, so that your worker class isn't infected with implementation details of the UI.