关闭来自不同线程的表单过早发生,导致异常

时间:2010-08-13 17:42:45

标签: winforms multithreading

我有以下情况,

Private Sub MyButton_Click(sender as Object, args as EventArgs) Handles MyButton.Click
    Me.pleaseWaitFrm = New PleaseWaitForm()
    ' Fire up new thread to do some work in (AddressOf DoMyWork)
    Me.pleaseWaitFrm.ShowDialog()
End Sub

Private Sub DoMyWork()
   Dim log = Me.DoTheActualWork()

   Me.pleaseWaitFrm.Close()

   Using logFrm as New LogViewer(log)
       logFrm.ShowDialog()
   End Using
End Sub

如果DoTheActualWork()来电退出的速度足够快,则会在Me.pleaseWaitFrm.Close()来电期间发出Me.pleaseWaitFrm.ShowDialog()来电。结果,毫不奇怪,是这个例外:

  

System.Windows.Forms.dll中出现未处理的“System.InvalidOperationException”类型异常

     

附加信息:在执行CreateHandle()时无法调用Value Close()。

显然,这是旧的“你不能。关闭()一个WinForm,而它正在加载”问题。但对我来说不明显的是如何最好地防止这种情况发生?如何安全可靠地延迟.Close(),直到在这种情况下这样做是安全的?

2 个答案:

答案 0 :(得分:2)

  

Me.pleaseWaitFrm.Close()

这是非法通话,您不能从另一个主题关闭表单。不知道你是如何逃避它的,当你使用调试器运行时应该引发IllegalOperationException。检查您的代码并删除对Control.CheckForIllegalCrossThreadCalls属性的任何分配。

你得到的这个例外是一个明显的副作用。您必须使用Control.Invoke()来关闭对话框。这会自动解决您的问题,在加载对话框之前,委托目标无法执行。

利用BackgroundWorker类,它使这很容易。您可以在RunWorkerCompleted事件处理程序中关闭该对话框。

答案 1 :(得分:1)

为什么不设置一个“ShouldClose”标志,可以在关闭表格时安全地检查 - 如果需要可以关闭它?

关于代码示例,我的实现方式略有不同,但请告诉我这是否违反了任何其他要求,我们可以修改它......

    ''in PleaseWaitForm:
    Public Property ShouldClose as boolean = false

Private Sub frmSplash_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Close()
End Sub

    ''Your posted code
    Private Sub MyButton_Click(sender as Object, args as EventArgs) Handles MyButton.Click
        Me.pleaseWaitFrm = New PleaseWaitForm()
        '' Fire up new thread to do some work in (AddressOf DoMyWork)
        Me.pleaseWaitFrm.ShowDialog()
    End Sub

    Private Sub DoMyWork()
       Dim log = Me.DoTheActualWork()


       Me.pleaseWaitFrm.ShouldClose = True
       If Me.pleaseWaitFrm.Created Then
           Me.pleaseWaitFrm.Created.Close
       End If

       Using logFrm as New LogViewer(log)
           logFrm.ShowDialog()
       End Using
    End Sub

简而言之,如果我们可以关闭表单,我们会这样做 - 否则,设置一个标志,表单将在完成加载时执行。

我在frmSplash.Load()中测试并且没有任何问题调用Me.Close但是如果你遇到任何问题,你可以通过在frmSplash上​​工作来检查值而不是使用加载事件

修改:发现并修复了错误