Preventing an event from firing after form close

S

Stu

In this form I subscribe to an event which sends data from a device
about every 10 milliseconds which the form then graphs in its event
handler. In my form closing event I unsubscribe my listener, but I'm
still receiving some messages after this point and it's causing a
crash due to my graph object already being disposed. What is the best
way to prevent this from happening?

1) One thing I tried was keeping a lock which sets a disposing flag
in my FormClosing event, then in my MessageReceived event I lock and
check the disposing flag before continuing. However this is causing a
deadlock when I try to close my form because within the lock in my
MessageReceived event I am invoking stuff on the UI thread, which is
sitting in its FormClosing event waiting for the lock to be released.

2) I've also tried just wrapping the meat of the MessageReceived
event in a try/catch, which "works" but the error that's happening in
my graph is for some reason causing major funkiness in other graphs on
other forms after I close this one. I'm using
System.Windows.Forms.DataVisualization.Charting.Chart for my graphing,
and after catching this error any time I try to use another Chart
instance on *any* form I get "A first chance exception of type
'System.NullReferenceException' occurred in
System.Windows.Forms.DataVisualization.dll" which doesn't crash the
system, but also isn't catchable (it just prints that out to the debug
window) and it slows the program waaaaayyyyy down. Closing the
program and reopening fixes this.

3) Should I maybe just try a wait of some kind to give the messages a
second to finish sending after I unsubscribe the event? I don't
suppose there's any way to know that there are messages pending?

I have googled the crap out of this, it's one of those problems that
everyone has and no one seems to have an answer to. Thanks in advance
for your help.
 
S

Stu

Short answer: turns out that while I never did put a lot on my C# blog,
this is one of the topics I did write about.  It's possible you would
find some useful information in the articles:http://msmvps.com/blogs/duniho/arch...duniho/archive/2008/09/12/form-closing-race-c...

Summary: use BeginInvoke() instead of Invoke(), and let .NET eat the
exception.

Regarding your observations…


Right, this is basically the same issue I focus on in my articles.
There are other ways around it, but if BeginInvoke() is suitable in your
case, it seems like the simplest approach.


Then there's a bug somewhere, probably in your code.

The place where the exception occurs should be the call to
Control.Invoke.  You should make sure that an exception from that call
either does not interrupt the whole processing of the message (i.e. just
becomes a "nop"), or that the whole processing of the message is safe to
interrupt without corrupting any data structures.


You should be able to enable "Always" for System.NullReferenceException
in the Debug/Exceptions… settings, to have the debugger interrupt the
process when that exception occurs.  Then you can look more closely at
it, to determine why it happens (and especially, what the stack trace is
when it happens).

I suspect you'll find that you're passing a null value to the library
for some reason, likely related to the improper handling of an exception
from Control.Invoke.


Your object that raises the event (sends the messages) would have to
implement a way to do this.  And keep in mind that "messages pending"
really means "have retrieved the event delegate reference"; they aren't
in a queue somewhere, it's just that the variable being used in a thread
raising the event has been initialized from the event field but the
delegate invocation hasn't completed yet.

Because of that, you still have a deadlock issue to solve, as you deal
with synchronization of whatever flag indicates the outstanding delegate
invocation.  You can probably get around the deadlock with a
sufficiently complex implementation, but it seems to me such a solution
is more complicated than is worth, given other simpler alternatives.

Another approach I didn't discuss in my articles is to use
Monitor.TryEnter.  Your OnFormClosing method can use this to attempt to
acquire the lock, and then give up if it fails.  On failure, it can
instead call BeginInvoke to attempt to close the form again, and set
e.Cancel before returning to prevent the current call from succeeding.

Hope that helps.

Pete


Awesome Pete, thanks very much for the help! I ended up going with
your last suggestion with Monitor.TryEnter in the FormClosing event
and it worked great.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top