HighMans HighMans - 5 months ago 31
Vb.net Question

Properly closing a form running a loop in a new thread

I have method in my form that is running a do while loop in a new thread.

That for loop exits once a sentinel value is set to true (set by another method that handles

Me.FormClosing
)

The issue is that when the form closes, I get occasionally get two exceptions.

ObjectDisposedException
and
ComponentModel.Win32Exception
.

How do I properly exit a form without "eating" these exceptions and ignoring them.

Code:

Dim _exit As Boolean

Public Sub test()
Dim task As Thread = New Thread(
Sub()
Do
checkInvoke(Sub() a.append("a"))
Loop While _exit = False
End Sub)
End Sub

Private Sub checkInvoke(ByVal _call As Action)
If Me.InvokeRequired Then
Me.Invoke(Sub() checkInvoke(_call))
Else
_call.Invoke()
End If
End Sub

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
_exit = True
End Sub

Answer

Where does the error come from ?

This can be a bit confusing but it actually is pretty logical...

  1. The user (or something else) closes the form.
  2. FormClosing is then called which sets _exit to True
  3. Then the Form closes itself, destroying its handle.
  4. Now, it depends where it sometimes throws an exception :
    • Either the Thread just finished the Invoke or the Loop, check the _exit value then ends the loop, everything goes fine.
    • Either it just began the Invoke, then it calls a method invoking the UI thread to modify something on the form that has just been disposed, no more Handle to this form, leading to ObjectDisposedException

How to prevent this ?

One thing you can do is, in your FormClosing event, wait for the Thread to end, then letting the system close the form :

Private _isFinished As Boolean = False
Private _exit As Boolean = False

Public Sub test()
    Dim task As Thread = New Thread(
            Sub()
                Do
                    checkInvoke(Sub() a.append("a"))
                Loop While _exit = False
                'We informed the UI thread we are done
                _isFinished = True
            End Sub)
End Sub


Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    _exit = True
    While Not _isFinished
        Application.DoEvent() 'We can't block the UI thread as it will be invoked by our second thread...
    End While
End Sub
Comments