Closing form from different thread happening too soon, causing exception
I've got the following situation,
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 (AddressO开发者_JAVA技巧f 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
If the DoTheActualWork()
call exits fast enough, the Me.pleaseWaitFrm.Close()
call is happening during the Me.pleaseWaitFrm.ShowDialog()
call. The result, no surprise, is this exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Additional information: Value Close() cannot be called while doing CreateHandle().
Obviously this is the old "you cannot .Close() a WinForm while it's in the process of loading" problem. But what isn't obvious to me is how best to prevent that from happening in this case? How does one safely and reliably delay the .Close() until it is safe to do so in this situation?
Me.pleaseWaitFrm.Close()
That's an illegal call, you are not allowed to close a form from another thread. Not sure how you got away with it, that should raise an IllegalOperationException when you run with a debugger. Review your code and delete any assignment to the Control.CheckForIllegalCrossThreadCalls property.
This exception you're getting is a clear side-effect of this. You must use Control.Invoke() to get the dialog closed. This automatically solves your problem, the delegate target cannot execute until the dialog is loaded.
Leverage the BackgroundWorker class, it makes this easy. You can close the dialog in a RunWorkerCompleted event handler.
Why not set a "ShouldClose" flag which can be checked when it's safe to close the form - and close it if required?
With regards to a code example, I'd implement it slightly differently but let me know if this breaks any other requirements and we can modify it...
''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
In short, if we Can close the form, we do - otherwise, set a flag and the form will do it when it finishes loading.
I tested and didn't get any issues calling Me.Close inside the frmSplash.Load() but if you do encounter any problems, you can make it cast-iron by having a worked on frmSplash which checks the value rather than use the Load event
Edit: Spotted and fixed a bug
精彩评论