Bug? Events from private member object still firing after it has been set to Nothing?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Joergen Bech

    Bug? Events from private member object still firing after it has been set to Nothing?


    I sometimes use delegates for broadcasting
    "StateChang ed" events, i.e. if I have multiple forms
    and/or controls that need updating at the same time
    as the result of a change in a global/common object,
    I keep local references to this object in each UI
    object, e.g.

    Private WithEvents _tools As RepeatTools

    and catch messages in an event handler like this:

    Private Sub _tools_StateCha nged(ByVal sender As Object, ByVal
    e As System.EventArg s) Handles _tools.StateCha nged
    If Me.AcceptEvents Then
    Try
    DisplayProperti es(sender)
    Finally
    Me.EnableEvents ()
    End Try
    End If
    End Sub

    In the past, I have had problems with _tools_StateCha nged
    events still firing after a form has been closed - as if parts of
    the form is still alive somewhere. Of course, once the event
    fires and code attempts to access the now disposed controls
    on the form, an exception is thrown.

    Until now, I have solved this by adding something like this:

    Private Sub frmRepeatPrevie w_FormClosing(B yVal sender As
    Object, ByVal e As System.Windows. Forms.FormClosi ngEventArgs) Handles
    Me.FormClosing
    _tools = Nothing
    End Sub

    to ensure that _tools would no longer fire any events in that
    form.

    HOWEVER: Now I have encountered a situation where
    _tools_StateCha nged is firing even though _tools has been
    set to Nothing, i.e. to fix it, I had to insert a "sentinel":

    Private Sub _tools_StateCha nged(ByVal sender As Object, ByVal
    e As System.EventArg s) Handles _tools.StateCha nged
    If _tools Is Nothing Then
    Return '<<<<<< Yes, we actually end up here!
    End If

    If Me.AcceptEvents Then
    Try
    DisplayProperti es(sender)
    Finally
    Me.EnableEvents ()
    End Try
    End If

    End Sub

    And now everything works, but I have plenty of code like
    this without such a check and I am concerned that something
    else might fail someday, so rather than hunt through my code
    to apply the same fix everywhere, could someone please explain
    to me what it is I do not understand and what I need to do to
    ensure that such delegates stop firing events?

    TIA,

    Joergen Bech



  • Rory Becker

    #2
    Re: Bug? Events from private member object still firing after it has been set to Nothing?

    Hello Joergen,

    Not knowing for certain how things work under the covers, this may be incorrect.

    It is simply an calculated guess.

    I do not know how handles works internally but let us suppose that it Translates
    to an AddHandler call early on in the construction of the object.

    We can then suppose that since the handler is hooked up to the instance of
    the object then your setting your reference to nothing does not allow the
    object to be disposed and so it remains able to generate events.

    If I am correct, you could replace your Handles with an addhandler call and
    call remove handler when you set _tools = nothing.

    You might be able to call removehandler anyway asthough you had called addhandlerwhich
    might disconnect the handles keyword.

    As I say all of this is (slightly calculated) guesswork.

    Perhaps it will help.

    --
    Rory


    Comment

    • Bill McCarthy

      #3
      Re: Bug? Events from private member object still firing after it has been set to Nothing?

      Hi Joergen,

      Just reading the responses to your post: seems there's a lot of confusion
      out there as to what WithEvents and declarative event handling in VB does.
      Hopefully this will shed some light upon that for the other folks here.....

      When you declare a field as WithEvents, VB actually creates a Property with
      that name, and a backing field with the same name prefixed by an
      underscore... in this case _tools would be the property name and it would
      have a backing field __tools. In the property Set, if __tools is not
      nothing, then any events declared declaratively via the Handles statement
      will be unwired for the reference. (RemoveHandler) , and if the new value is
      not nothing, then the events will be wired up.

      So given this, your setting _tools = Nothing will unwire the event handlers.
      If however you wrote __tools = Nothing, you would not unwire the event
      handlers, and in fact you would prevent VB from being able to unwire. The
      extra _ can be an un-doing of the best of code ;)

      You also may be experiencing race conditions in that even though you have
      attempted to unwire, an event invocation may already be queued. This is
      unlikely, but if it is just one extra event immediately after the form is
      closed, it can happen.







      "Joergen Bech" <jbech@post1.te le.dkwrote in message
      news:g83nu3dna7 r6b4f6t1lr5tone 90544v3lv@4ax.c om...
      >
      I sometimes use delegates for broadcasting
      "StateChang ed" events, i.e. if I have multiple forms
      and/or controls that need updating at the same time
      as the result of a change in a global/common object,
      I keep local references to this object in each UI
      object, e.g.
      >
      Private WithEvents _tools As RepeatTools
      >
      and catch messages in an event handler like this:
      >
      Private Sub _tools_StateCha nged(ByVal sender As Object, ByVal
      e As System.EventArg s) Handles _tools.StateCha nged
      If Me.AcceptEvents Then
      Try
      DisplayProperti es(sender)
      Finally
      Me.EnableEvents ()
      End Try
      End If
      End Sub
      >
      In the past, I have had problems with _tools_StateCha nged
      events still firing after a form has been closed - as if parts of
      the form is still alive somewhere. Of course, once the event
      fires and code attempts to access the now disposed controls
      on the form, an exception is thrown.
      >
      Until now, I have solved this by adding something like this:
      >
      Private Sub frmRepeatPrevie w_FormClosing(B yVal sender As
      Object, ByVal e As System.Windows. Forms.FormClosi ngEventArgs) Handles
      Me.FormClosing
      _tools = Nothing
      End Sub
      >
      to ensure that _tools would no longer fire any events in that
      form.
      >
      HOWEVER: Now I have encountered a situation where
      _tools_StateCha nged is firing even though _tools has been
      set to Nothing, i.e. to fix it, I had to insert a "sentinel":
      >
      Private Sub _tools_StateCha nged(ByVal sender As Object, ByVal
      e As System.EventArg s) Handles _tools.StateCha nged
      If _tools Is Nothing Then
      Return '<<<<<< Yes, we actually end up here!
      End If
      >
      If Me.AcceptEvents Then
      Try
      DisplayProperti es(sender)
      Finally
      Me.EnableEvents ()
      End Try
      End If
      >
      End Sub
      >
      And now everything works, but I have plenty of code like
      this without such a check and I am concerned that something
      else might fail someday, so rather than hunt through my code
      to apply the same fix everywhere, could someone please explain
      to me what it is I do not understand and what I need to do to
      ensure that such delegates stop firing events?
      >
      TIA,
      >
      Joergen Bech
      >
      >
      >

      Comment

      • Joergen Bech

        #4
        Re: Bug? Events from private member object still firing after it has been set to Nothing?


        Thank you. I'll try the explicit Add/Remove approach and remove
        the WithEvents keyword so I can control the process.

        It is probably a bad idea to keep the WithEvents keyword and use
        AddHandler "just in case". Unclean and unsafe in my eyes.

        Regards,

        Joergen Bech



        On Thu, 27 Mar 2008 12:48:02 +0000 (UTC), Rory Becker
        <rorybecker@new sgroup.nospamwr ote:
        >Hello Joergen,
        >
        >Not knowing for certain how things work under the covers, this may be incorrect.
        >
        >It is simply an calculated guess.
        >
        >I do not know how handles works internally but let us suppose that it Translates
        >to an AddHandler call early on in the construction of the object.
        >
        >We can then suppose that since the handler is hooked up to the instance of
        >the object then your setting your reference to nothing does not allow the
        >object to be disposed and so it remains able to generate events.
        >
        >If I am correct, you could replace your Handles with an addhandler call and
        >call remove handler when you set _tools = nothing.
        >
        >You might be able to call removehandler anyway asthough you had called addhandlerwhich
        >might disconnect the handles keyword.
        >
        >As I say all of this is (slightly calculated) guesswork.
        >
        >Perhaps it will help.

        Comment

        • Joergen Bech

          #5
          Re: Bug? Events from private member object still firing after it has been set to Nothing?


          On Fri, 28 Mar 2008 00:47:26 +1100, "Bill McCarthy" <Bill@NOSPAM.co m>
          wrote:
          >Hi Joergen,
          >
          >Just reading the responses to your post: seems there's a lot of confusion
          >out there as to what WithEvents and declarative event handling in VB does.
          >Hopefully this will shed some light upon that for the other folks here.....
          >
          >When you declare a field as WithEvents, VB actually creates a Property with
          >that name, and a backing field with the same name prefixed by an
          >underscore.. . in this case _tools would be the property name and it would
          >have a backing field __tools. In the property Set, if __tools is not
          >nothing, then any events declared declaratively via the Handles statement
          >will be unwired for the reference. (RemoveHandler) , and if the new value is
          >not nothing, then the events will be wired up.
          I took a closer look using Reflector and you are right (which I don't
          have to tell you). For others reading this thread, it looks like this:

          Private Overridable Property _tools As RepeatTools
          Get
          Return Me.__tools
          End Get
          Set(ByVal WithEventsValue As RepeatTools)
          If (Not Me.__tools Is Nothing) Then
          RemoveHandler Me.__tools.Stat eChanged, New
          StateChangedEve ntHandler(Addre ssOf Me._tools_State Changed)
          End If
          Me.__tools = WithEventsValue
          If (Not Me.__tools Is Nothing) Then
          AddHandler Me.__tools.Stat eChanged, New
          StateChangedEve ntHandler(Addre ssOf Me._tools_State Changed)
          End If
          End Set
          End Property

          and

          "_tools = Nothing"

          actually becomes

          "Me._tools = Nothing"

          which means that the private property setter is called and the
          event handler is, in fact, explicitly removed immediately, just by
          using "_tools = Nothing".

          This also means that removing the WithEvents keyword and adding/
          removing event handlers explicitly is not going to make a bit of
          difference if everything else is kept the same (i.e. if I keep the
          same flow of statements/events).
          >So given this, your setting _tools = Nothing will unwire the event handlers.
          >If however you wrote __tools = Nothing, you would not unwire the event
          >handlers, and in fact you would prevent VB from being able to unwire. The
          >extra _ can be an un-doing of the best of code ;)
          I can safely say that although I start all my non-public member
          variables with an underscore, there are no double underscores
          in my source. I would not do such a thing :)
          >You also may be experiencing race conditions in that even though you have
          >attempted to unwire, an event invocation may already be queued. This is
          >unlikely, but if it is just one extra event immediately after the form is
          >closed, it can happen.
          You hit the nail spot on the head with that last comment.

          The form/member destruction does indeed happen in response to a
          StateChanged event happening for a reference to the same object
          but in another form. That other form then closes the form we are
          talking about, leaving a queued event that insists on firing in
          limbo.

          My original "sentinel" solution takes care of the problem, but
          the proper solution would be to take care of the architectural flaw
          that allows this to happen.

          All my fault.

          Regards,

          Joergen Bech





          Comment

          Working...