Reputation: 131
On the main form of my app, I have one event handler that is called from a different thread than UI because it is an event generated by a hardware device. To sync. with the GUI thread, when the event is called, I do Form.BeginInvoke(). The call is then queued in the Message Loop.
When the user needs to close the form, before closing I remove the handler for the Event, but it seems that invoked calls could still be called. Then if the routine to handle the event uses some information that is no longer available at the time that it is called, I can have problems.
For example:
Private MyDevice as New SomeDevice()
Private MyGlobalVar as MyVarType
Public Sub OnDeviceEvent()
If InvokeRequired Then
BeginInvoke(Sub() OnDeviceEvent())
Return
End If
If MyGlobalVar.Field = 0 then
'do something
end if
End Sub
Public Sub PrepareToCloseForm()
'removes the handler of the event
RemoveHandler MyDevice.DeviceEvent, AddressOf OnDeviceEvent
MyGlobalVar = Nothing
End Sub
Using the code above, right after I run PrepareToCloseForm() sometimes I get an object Null error on the following line:
If MyGlobalVar.Field = 0 then
I could do a Null check before I use the variable, but as I have many other events, I would like a more elegant solution. How can I make sure that Invoked calls will not happen after I removed the handler?
Should I call DoEvents before I remove all the handlers to process pending messages?
Upvotes: 3
Views: 648
Reputation: 924
The problem you have is that if you closed the thing and set MyGlobalVar to Nothing, even if the OnDeviceEvent checks that, it could become nothing at any time while you're still trying to use it. To get around this, you could put a SyncLock around access to MyGlobalVar
Private MyDevice as SomeDevice()
Private MyGlobalVar as MyVarType
Private SyncLockObject as New Object
Public Sub OnDeviceEvent()
If InvokeRequired Then
BeginInvoke(Sub() OnDeviceEvent())
Return
End If
SyncLock SyncLockObject
If MyGlobalVar IsNot Nothing Then
If MyGlobalVar.Field = 0 then
'do something
End If
End If
End SyncLock
End Sub
Public Sub PrepareToCloseForm()
'removes the handler of the event
RemoveHandler MyDevice.DeviceEvent, AddressOf OnDeviceEvent
SyncLock SyncLockObject
MyGlobalVar = Nothing
End SyncLock
End Sub
That way, you can't set MyGlobalVar to nothing while OnDeviceEvent is accessing it and you can't access it OnDeviceEvent can't access it while PrepareToCloseForm is setting it to Nothing
Upvotes: 3