Giovani Luigi
Giovani Luigi

Reputation: 131

.NET Remove event handler and destroy invoked events

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

Answers (1)

dwilliss
dwilliss

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

Related Questions