【发布时间】:2014-07-11 18:48:33
【问题描述】:
我有一个运行后台作业的线程,需要不时更新 GUI。我的程序被设计成当用户单击表单时,线程和后台操作仍然运行,但控件已被释放(用于内存管理目的)。
我一直在使用 Invoke() 和“If Control.Created = True”来确保线程可以成功更新控件而不会遇到任何异常。但是,当重新创建表单时,所有“Control.Created”值都为 false,并且 Invoke() 失败并显示“{“在创建窗口句柄之前无法在控件上调用 Invoke 或 BeginInvoke。”}”
我的猜测是,这与重新创建表单时分配了不同的句柄以及“Invoke()”正在查看旧句柄这一事实有关。所以我的问题是,我该如何解决这个问题?
编辑:根据要求,打开表单的代码以及 bg 线程的工作位置
打开 DropLogMDIalt 表单很简单
FormCTRL.Show()
后台线程在控件被修改时运行,因此 NumericUpDown 大于 0(以便有一些东西可以倒计时)
Private Sub NLauncherTerminateInput_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DLScanInterval.ValueChanged
If DLScanInterval.Created = True Then
DLTimerControlValue = DLScanInterval.Value
If DLTimerControlValue = 0 Then
CancelDropLogTimer()
Else
If DLScanIntervalControl.Active = False Then
BeginDropLogTimer()
End If
End If
End If
End Sub
Public Sub BeginDropLogTimer()
Dim N As New Threading.Thread(AddressOf DropLogTimerIntervalThreadWorker)
N.Start()
DLScanIntervalControl.ThreadID = N.ManagedThreadId
DLScanIntervalControl.Active = True
End Sub
Public Sub CancelDropLogTimer()
DLScanIntervalControl.Active = False
End Sub
Public Sub DropLogTimerIntervalThreadWorker()
DLScanTimerSecondsLeft = DLTimerControlValue * 60
Dim s As Integer = DLTimerControlValue
Do Until 1 = 2
DLScanTimerSecondsLeft = DLTimerControlValue * 60
Do Until DLScanTimerSecondsLeft <= 0
If Not (DLTimerControlValue = 0 Or DLScanIntervalControl.CancelPending = True) Then
Else
Exit Sub
End If
If Not DLTimerControlValue = s Then
DLScanTimerSecondsLeft = DLTimerControlValue * 60
s = DLTimerControlValue
End If
Dim ToInvoke As New MethodInvoker(Sub()
Timer(DLScanTimerSecondsLeft, ":", DLScanIntervalTB)
End Sub)
If (Me.IsHandleCreated) Then
If (InvokeRequired) Then
Invoke(ToInvoke)
Else
ToInvoke()
End If
End If
Threading.Thread.Sleep(1000)
DLScanTimerSecondsLeft -= 1
Loop
CompareScan = True
PerformScan()
Loop
End Sub
通过声明一个新的 thread.thread 来简单地调用线程,但是,我创建了一个类和一个变量,该线程用于检查它是否应该仍在运行(类似于 backgroundworker 的方式)这是由“DLScanIntervalControl.CancelPending”说明
表格随后由
关闭Form.Close()
如果用户点击一个标签,然后使用与上面所示相同的方法(FormCTRL.Show()),它可以重新打开
【问题讨论】:
-
我怀疑如果您再次创建控件(包括表单),那么它们与以前的控件不同(即它们具有不同的句柄)。你可以只用
.Hide适当的表格来代替吗? ...啊,是的,来自Form.Close() 的文档:“当关闭表单时,在对象中创建的所有资源都将关闭并释放表单。” -
我在尝试使用委托时遇到了类似的错误,我发现只有在“需要”时才创建句柄。我不确定什么构成“需要”,但您可以通过访问对象的 Handle 属性来强制它创建句柄。我在我的应用程序中必须做的是遍历表单上的所有控件并检查 IsHandleCreated 属性。如果它是假的,那么我只是调用“Dim tmp = ctrl.Handle”并继续我的快乐方式。它相当贫民窟,但它可以在这里帮助你(如果你仍然需要帮助)
-
这是一个典型的例子,说明为什么 UI 应该禁止其他线程。而不是增加后台线程的开销来更新可能存在的 UI。让表单本身以受控的方式适当地处理更新。
标签: vb.net multithreading winforms invoke