Exception management question...

J

Jay B. Harlow [MVP - Outlook]

David,
I believe he is having this problem because he subscribed to the
ThreadException from a static method (main) and not from an instance method
in FormA.
Application.ThreadException is a static event! Remember events can be
instance or static, and their handlers can be instance or static. Events
really don't care either way, raising an Event simply calls all the handlers
(delegates) that have been registered with it...
At the time he subscribed there was no window at all in the system, so
perhaps the Forms class is getting confused about where to deliver the
event to.
Again Application.ThreadException is a static event, it is "delivered" to
all the handlers that are currently registered with it.
if there was no window or pump running at the time the event was
subscribed to it may getting confused
I'm really not sure why! The event (any event) is implemented as a Delegate,
Delegates really don't know any thing about windows or pumps.
it may be expecting an instance and there is none associated with the
event.
Events don't expect instances, events expect handlers, these handlers can be
either instance methods or static methods, the event (more importantly the
underlying delegate) doesn't care what kind of method it (the delegate) is
associated with. Which IMHO is one of the truly cool & powerful features of
..NET events over COM & VB6 events!


I would expect the same result (as I described & Craig perceived) even if
LaunchFormB was called within a Form because of a Button Click event. In
fact that is the way I understand that Craig is calling it.

Hope this helps
Jay
 
C

craig

David,

I just tested your idea and it appears that your thinking is correct.
Previously, the entry point to my application looked as follows:

static void Main()
{
//CLR unhandled exceptions
AppDomain.CurrentDomain.UnhandledException +=new
UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

//Windows forms unhandled exceptions
Application.ThreadException +=new
ThreadExceptionEventHandler(Application_ThreadException);

Application.Run(new FormMain());
}

When the code is run in this configuration, an exception thrown from within
the FormB_Load() event handler is handled in the
Application_ThreadException() event handler rather than the catch block in
FormA as desired.

However, if I move the line of code which wires up the thread exception
event handler to the FormMain_Load() instance method as you suggested and
run the application, an exception thrown from within the FormB_Load() event
handler is handled in the catch block in FormA as desired.

I also tried moving this line of code to the FormMain constructor rather
than the FormMain_Load() event handler. In that case, the problem was not
corrected. It only seems to work from within the FormMain_Load() event
handler.

Thus, the new configuration looks as follows:

static void Main()
{
//CLR unhandled exceptions
AppDomain.CurrentDomain.UnhandledException +=new
UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

Application.Run(new FormMain());
}

private void FormMain_Load(object sender, System.EventArgs e)
{
//Windows forms unhandled exceptions
Application.ThreadException +=new
ThreadExceptionEventHandler(Application_ThreadException);
}

It would be interesting to determine exactly why this configuration works
and the other does not. Also, in this configuration, it would be
interesting to throw a ThreadException in order to see if it would be
handled in Application_ThreadException() as it should. Do you have any
thoughts regarding why the desired behavior is not acheived by placing the
line of code in the main form constructor??
 
D

David Levine

Jay B. Harlow said:
David,
Application.ThreadException is a static event! Remember events can be
instance or static, and their handlers can be instance or static. Events
really don't care either way, raising an Event simply calls all the
handlers (delegates) that have been registered with it...

I know about the differences between static and instance delegates. However,
this is not a question about how delegates are implemented, it is a question
about how the Forms class handles exceptions occurring on that one thread
and when it actually fires the event. I haven't spent a lot of time digging
into the internals but it is not straightforward. The Forms class, and how
it handles threads, looks like a bit of black art.
Again Application.ThreadException is a static event, it is "delivered" to
all the handlers that are currently registered with it.

That affects to whom the event is delivered, (static versus instance
methods), not how it is generated in the first place.

I'm really not sure why! The event (any event) is implemented as a
Delegate, Delegates really don't know any thing about windows or pumps.
Agreed.

Events don't expect instances, events expect handlers, these handlers can
be either instance methods or static methods, the event (more importantly
the underlying delegate) doesn't care what kind of method it (the
delegate) is associated with.

I was not referring to the delegate itself, but the code that determined
whether or not to fire the event.

I would expect the same result (as I described & Craig perceived) even if
LaunchFormB was called within a Form because of a Button Click event. In
fact that is the way I understand that Craig is calling it.

We are talking apples and oranges. The real question is: under what
circumstances will the Forms class catch an exception and forward it to the
Application.ThreadException event, and when will it allow other mechanisms
to handle it. Once the Forms class actually fires the ThreadException
everything works as you describe, but the problem is that it is never firing
in the first place.

regards,
Dave
 
D

David Levine

craig said:
David,

I just tested your idea and it appears that your thinking is correct.
Good.

<snip>
When the code is run in this configuration, an exception thrown from
within the FormB_Load() event handler is handled in the
Application_ThreadException() event handler rather than the catch block in
FormA as desired.

However, if I move the line of code which wires up the thread exception
event handler to the FormMain_Load() instance method as you suggested and
run the application, an exception thrown from within the FormB_Load()
event handler is handled in the catch block in FormA as desired.

I also tried moving this line of code to the FormMain constructor rather
than the FormMain_Load() event handler. In that case, the problem was not
corrected. It only seems to work from within the FormMain_Load() event
handler.

It appears that it wants to be associated with a Form object and is not
really designed to operate otherwise. It might also need a functioning
message pump at the time the event is subscribed to, which may be why
subscribing to it in the ctor does not work (this is another WAG). On the
whole, I'd call this one a bug - it certainly doesn't work in a reasonable
manner, and it is clearly not documented about any restrictions regarding
when or how to subscribe to the event.

It would be interesting to determine exactly why this configuration works
and the other does not. Also, in this configuration, it would be
interesting to throw a ThreadException in order to see if it would be
handled in Application_ThreadException() as it should. Do you have any
thoughts regarding why the desired behavior is not acheived by placing the
line of code in the main form constructor??

Yes, it's called a "bug" and MSFT needs to fix it! All we can do is work
around it and avoid the problem.

There are several places in the CLR where you can subscribe to an event and
nothing will ever happen. One such is when subscribing to the
UnhandledException event...if the code is not running in the default
appdomain then the event will never get delivered - it only works in the
default appdomain. There are several other cases like this that slip my mind
at the moment.

In my opinion if the event itself can determine that the subscription will
not perform as expected it ought to throw an exception or otherwise signal
that even though mechanically the subscriber is connected to the event, the
event itself will never get delivered. Otherwise you have the worst of all
possible worlds - the illusion that something works. The code will pass a
code review and never detect a problem, but it simply will not work.

regards,
Dave
 
C

craig

Thanks again, David. I can't tell you how much I appreciate your taking the
time to describe this work-around.

I wonder if it wouldn't make sense to pass this info on to Jason Clark, the
guy who wrote the MSDN article on global exception handling.
 
C

craig

By the way....I was just wondering if you have any kind of a .NET weblog
that I could check out??
 
D

David Levine

craig said:
Thanks again, David. I can't tell you how much I appreciate your taking
the time to describe this work-around.

I wonder if it wouldn't make sense to pass this info on to Jason Clark,
the guy who wrote the MSDN article on global exception handling.

I appreciate the thought but I've found that unsolicited advice is usually
the worst kind to give. But thanks.

And in response to your other message, I just started a weblog recently -
haven't done much with it yet but here it is anway...
http://wiscdave.blogspot.com/
 
C

craig

Wow....the list of discussion topics on your blog looks excellent. I am
looking forward to reading...

Thanks again, Dave.
 

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

Similar Threads


Top