vb.net调用问题

时间:2014-07-11 18:48:34

标签: vb.net multithreading winforms invoke

我有一个运行后台作业的线程,需要偶尔更新一次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简单地调用该线程,但是,我创建了一个类和一个变量,线程用它来检查它是否应该仍在运行(类似于后台工作者的方式)这是由“DLScanIntervalControl.CancelPending”

说明

稍后由

关闭表单
Form.Close()

如果用户点击标签然后使用与上面所示相同的方法(FormCTRL.Show()),则可以重新打开它。

3 个答案:

答案 0 :(得分:1)

来自MSDN

  

“如果控件的句柄尚不存在,InvokeRequired会搜索控件的父链,直到找到一个具有窗口句柄的控件或窗体。如果找不到合适的句柄,则InvokeRequired方法返回false 。“

换句话说,您需要验证是否已创建句柄,然后检查是否需要调用。

If(Me.IsHandleCreated) Then
    If(Me.InvokeRequired) Then
        '...
    Else
        '...
    End If
End If

答案 1 :(得分:1)

尝试使用委托更新另一个线程中的表单上的控件时,我遇到了类似的错误。我发现只有在需要&#34;时才会创建句柄。我不确定是什么构成&#34;需要&#34;,但您可以通过访问对象的Handle属性来强制它创建句柄。 我在申请表中所做的是:

  ' Iterate through each control on the form, and if the handle isn't created yet, call the
  ' Handle property to force it to be created
  For Each ctrl As Control In Me.Controls
     While Not ctrl.IsHandleCreated
        Dim tmp = ctrl.Handle
        tmp = Nothing
     End While ' Not ctrl.IsHandleCreated
  Next ' ctrl As Control In Me.Controls

它相当贫民窟,但它可能对你有帮助(如果你还需要帮助)

答案 2 :(得分:0)

我认为这里的问题与调用无关,而是引用。遵循这个伪代码...

Dim A As New Form1
A.Show()

''Spawn background thread with a reference to A

A.Dispose()

Dim B As New Form1
B.Show()

线程正在尝试引用上面放置的Form1的第一个实例,并且将始终保持这种状态。

如果您希望线程能够更新任何表单,那么您需要为线程提供(同步)方式来引用表单...

Public Class Worker

    Private Target As Form1
    Private TargetLock As New Object

    Public Sub SetTargetForm(Frm as Form1)
        SyncLock TargetLock
            Target = Frm
        End SyncLock
    End Sub

    Public Sub DoWork() ''The worker thread method
        ''Do work as usual then...
        SyncLock TargetLock
            If Target IsNot Nothing AndAlso Target.IsHandleCreated Then
                If Target.InvokeRequired
                    Target.Invoke(...)
                Else
                    ...
                End If
            End If
        End SyncLock
    End Sub
End Class

这样,当新表单可用时,您可以使用SetTargetForm()通知工作线程,它将更新相应的表单。

当然,为了简单/可维护性,你最好重构“更新UI”检查并调用不同方法的调用,但是你明白了。

请注意,我没有手持IDE,因此可能存在拼写错误。

最后一点......我质疑为了内存管理目的而处理表单的价值。控件在内存方面相当轻量级,而且表单使用的对象比表单本身更常见。你确定你为这种增加的复杂性获得了真正的好处吗?