Exception management question...

C

craig

I am wondering if there are some best practices for determining a strategy
for using try/catch blocks within an application.

My current thoughts are:

1. The code the initiates any high-level user tasks should always be
included in a try/catch block that actually handles any exceptions that
occur (log the exception, display a message box, etc.).

2. Low-level operations that are used to carry out the high level tasks
should make use of try/catch blocks where necessary, not for the purpose of
handling the exceptions, but for the purpose of raising exceptions and
building a set of nested exceptions (exceptions with inner exceptions, with
inner exceptions, etc.) that help to describe and clarify any errors that
occur. This way, when the nested exceptions are finally handled within the
high-level tasks, there will be sufficient information to describe the
problem.

Does this sound like the correct approach? Are there any good MSDN articles
on this subject?
 
J

Jay B. Harlow [MVP - Outlook]

Craig,
In addition to Marek's comments.
1. The code the initiates any high-level user tasks should always be
included in a try/catch block that actually handles any exceptions that

Disagree: In an effort to avoid duplicate code (try/catch/log exception) I
tend to leave exception logging to my global exception handlers, instead I
use try/finally & using statements to ensure any resources are cleaned up.
2. Low-level operations that are used to carry out the high level tasks
should make use of try/catch blocks where necessary, not for the purpose
of handling the exceptions, but for the purpose of raising exceptions and
building a set of nested exceptions (exceptions with inner exceptions,
with
Agree: I will use a try/catch when there is something specific to do, such
as a retry on FileNotFound, or throwing context sensitive exceptions. IMHO
logging the exception is not specific enough...


Depending on the type of application you are creating, .NET has three
different global exception handlers.

For ASP.NET look at:
System.Web.HttpApplication.Error event
Normally placed in your Global.asax file.

For console applications look at:
System.AppDomain.UnhandledException event
Use AddHandler in your Sub Main.

For Windows Forms look at:
System.Windows.Forms.Application.ThreadException event
Use AddHandler in your Sub Main.

It can be beneficial to combine the above global handlers in your app, as
well as wrap your Sub Main in a try catch itself.

There is an article in the June 2004 MSDN Magazine that shows how to
implement the global exception handling in .NET that explains why & when you
use multiple of the above handlers...

http://msdn.microsoft.com/msdnmag/issues/04/06/NET/default.aspx

For example: In my Windows Forms apps I would have a handler attached to the
Application.ThreadException event, plus a Try/Catch in my Main. The
Try/Catch in Main only catches exceptions if the constructor of the MainForm
raises an exception, the Application.ThreadException handler will catch all
uncaught exceptions from any form/control event handlers.

Hope this helps
Jay
 
C

craig

This is a great article. I have used it when I needed to add new exception
classes of my own.
 
C

craig

Quite a bit to chew on here. I wonder how what you are describing here
squares up with the approach outlined in the Exception Management
Architecture Guide listed in Joakim's post.
 
D

David Levine

Jay B. Harlow said:
Craig,
In addition to Marek's comments.


Disagree: In an effort to avoid duplicate code (try/catch/log exception) I
tend to leave exception logging to my global exception handlers, instead I
use try/finally & using statements to ensure any resources are cleaned up.

There are lots of ways of solving problems - if it works for your
application that's great but I disagree with this as a general way of
handling exceptions.
 
D

David Levine

Jay,

I've read that article, as well as numerous others on exceptions. I disagree
with using the UE as your primary mechanism for reporting or dealing with
exceptions.

Here's a simple scenario where subscribing to the UE as your main handler
wont work...a launcher creates a 2nd appdomain and uses that to run your
windows form in...the windows form does not know that it is running in a 2nd
appdomain. It subscribes to the UE. However, in the 1.x runtime only
handlers in the default appdomain actually get the nofication and you wont
even get an error notification that the subcription attempt is a no-op; and
who knows what the launcher actually does with it?... probably be the wrong
thing anyway...result is a UE gets dropped.

Let's say that the launcher does subscribe to the UE and you want that to be
your primary handler. Now when a UE happens in the 2nd appdomain it gets
serialized across the appdomain boundary before it gets handled by the
default appdomain's UE handler. Let's say the exception happens to be a
custom exception type defined by an assembly that is only accessible via the
loading rules in effect in the 2nd appdomain. The result is that another
exception will occur when the runtime tries to deserialize the original
exception type at the appdomain boundary. The result is that you will
actually get a TypeLoadException instead of the original custom exception.

In other words, using the UE as your main mechanism for dealing with
exceptions already wont work in all cases, and as the runtime evolves it is
even less likely to be a viable means of dealing with exceptions, other then
as a last-ditch attempt to notify the user that something bad happened and
leave some trouble-shooting breadcrumbs behind. Bottom line... it may work
well for simple apps but I would not use it in a large, mission critical
app.

Hope this helps, and regards,
Dave
 
J

Jay B. Harlow [MVP - Outlook]

David,
If my app used a launcher, the launcher would be responsible for the UE, so
I really don't see your point.

I guess we will need to agree to disagree.

Hope this helps
Jay
 
D

David Levine

What if the app did not even know it was being run under a launcher? Also,
how do you determine that the code is correct, especially during a code
review? It's hard enough to look at a piece of code and determine that it
has correct error handling...pushing the recovery off to some other piece of
code, which may or may not even execute, opens the app up to very subtle,
hard to reproduce/fix problems. The strategy may work for small, simple
apps, but it does not scale.

I agree to disagree. Each time you make this recommendation to others I will
disagree.
 
J

Jay B. Harlow [MVP - Outlook]

David,
First I'm curious: Are you referring strictly to
AppDomain.UnhandledException or to all three Global Exception Handlers? As I
was referring to all three as a whole.
What if the app did not even know it was being run under a launcher?
Consider the flip side. How does the launcher know the app you are calling
has its own exception handling? I hope you don't think the writer of the
launcher can "blindly" trust all the apps being launched are correctly
written so it doesn't need any exception handling!
The strategy may work for small, simple apps, but it does not scale.
One could argue that all apps should be small & simple, especially ones that
are meant to scale. As I hope you agree that complexity leads to very
subtle, hard to reproduce/fix problems... So if using the Global Exception
Handlers reduces the amount of complexity in my code, then I will use them
unless said code is not compatible with the Global Exception Handlers! (such
as asynchronous methods). (In other words is the class half empty or is it
really half full?)
pushing the recovery off to some other piece of code, which may or may not
even execute, opens the app up to very subtle, hard to reproduce/fix
problems
I agree, pushing the recovery off to the program being launched, which can
easily be forgotten, opens the app up to very subtle, hard to reproduce/fix
problems. Where as a Global Exception Handlers helps ensures that someone is
handling the exception, whether its the launchee or the launcher. I'm saying
"helps ensures" as I've seen where exceptions have been missed by even a
Global Exception Handlers, such as asynchronous methods. In other words if
anything I'm suggesting you really should have both! Especially if you are
using a launcher!

It appears (based on your earlier reference to UE) you are only referring
strictly to AppDomain.UnhandledException, where as I am referring to all
three Global Exception Handlers; AppDomain.UnhandledException,
HttpApplication.Error and Application.ThreadException. Plus I am suggesting
that you should use the one that is appropriate, I find
AppDomain.UnhandledException not to be very useful in most Windows Forms or
Web Forms applications. Instead I find Application.ThreadException to be
extremely useful in Windows Forms applications, and HttpApplication.Error &
Page.Error (TemplateControl.Error) to be extremely useful in Web Forms
applications. In other words: Pick the right tool for the right job!
I agree to disagree. Each time you make this recommendation to others I
will disagree.
You are perfectly welcome to! If fact I hope you do, after all I am from a
free country that prides itself on freedom of speech. It does not make
either of our statements any more or less correct then the others statement.
In fact I actually agree you have a point, that the Global Exception
Handlers are not correct all the time, although I suspect they are correct
more times then you feel they are. I will save your posts and see about
rolling the intent of your comments into my FAQ.

BTW: If you re-read my statements I never stated that using Global Exception
Handlers are an absolute, I simply stated that is what I tended (normally)
used! I hope you agree that not using should not be an absolute either!

Hope this helps
Jay
 
D

David Levine

First I'm curious: Are you referring strictly to
AppDomain.UnhandledException or to all three Global Exception Handlers? As
I was referring to all three as a whole.

As was I. BTW, there is only one true UE, the AppDomain.UE - the others are
actually events generated by different libraries that wrap execution threads
and generate the event when they catch an exception; typically the exception
gets swallowed. Some appear to use the subscription to the event as a
filter - if there is a subscription then the exception gets swallowed and
the event generated; if there are no subscriptions the exception propagates
up the call stack. It's been a while since I did the investigation and I
don't recall the details of which one does what.

Consider the flip side. How does the launcher know the app you are calling
has its own exception handling? I hope you don't think the writer of the
launcher can "blindly" trust all the apps being launched are correctly
written so it doesn't need any exception handling!

Of course the launcher does not make any assumptions whatsoever about the
code it launched - it has its own error handling to guard against buggy
apps.
One could argue that all apps should be small & simple, especially ones
that are meant to scale.

One could make that argument, but it's silly to do so. Large problems
require large solutions, and not all problems, or applications, can be
reduced to a small, trivial size. You can modularize it, componentize it,
etc., and these are all good practices, but the fact remains that the parts
must be assembled into a whole. This is especially problematic with
exceptions because you now have a system comprised of separately designed
and built components (perhaps in different continents) that must
interoperate with each other, yet the effects of an unhandled event (a UE)
may be felt far beyond the boundaries of that component. It is the
non-local nature of exceptions that makes them so powerful and also so
dangerous.

Perhaps we have a philosophical disagreement.

I regard an exception as an error condition and I regard an unhandled
exception as a programming error. This states it in absolute terms and I
will also state that there are always exceptions to this rule, but in most
cases I find this to be accurate.

All execution threads should have a exception handler somewhere, if only to
ensure proper reporting and propagation of the error, and all code paths
must be known and testable. It is also desirable to make the code as
readable and understandable as possible, if for no other reason then the
make it easier to spot defects during a code review - using UEs to
communicate results makes this harder.

That is not to say that all exceptions should be swallowed...the exceptions
should continue to propagate until a handler is reached to deal with the
particular exception - ultimately to deal with all exceptions. It is only
that thread that has sufficient context to make the most correct decision on
how to proceed (e.g. Abort/retry/cancel). A UE handler simply has no context
to evaluate the situation.

The exception mechanism is also a uni-directional channel for information to
flow up the call stack - it reports on errors and other exceptional
conditions. It is loosely typed and does not provide a signature against
which a compiler can enforce usage, other then a generic Exception type (and
even that is not guaranteed, as you can catch, and presumably throw,
non-Exception derived types).

Exceptions are not a resource that can be dealt with the way that memory or
other managed resources are treated; there is no execution GC to correctly
clean up after a faulted thread. It is way too easy to get cleanup details
wrong. Sure, you can use finally blocks (and you should) for
transaction-oriented cleanup, but these are not always guaranteed to run,
and even if they do, the code must still be proven to be correct, and often
it isn't. The situation gets even more complex and murkier when there are
numerous asynchronous threads and interop involved, when nested exceptions
occur, when aborts get injected into threads, multiple appdomains are
involved, remoting, partially completed methods, objects left in an
indeterminate state, etc. It gets further complicated when adding in the
effects of versioning and system evolution.

Exception handling is a very complex subject and it cannot be reduced to a
few paragraphs here. It is tightly bound up with execution and thread
control, performance, correctness, robustness, and reliability. It doesn't
help that the tools to analyze data flow and execution control in
conjunction with exceptions are practically non-existent - it must be
hand-analyzed, and we all know the pitfalls of that.

I believe that using UEs for error handling results in less reliable, less
robust systems; there will be a natural tendency to ignore most exceptions
because something else, somewhere else, will deal with it...and this will
usually be the wrong way to deal with it.
As I hope you agree that complexity leads to very subtle, hard to
reproduce/fix problems... So if using the Global Exception Handlers
reduces the amount of complexity in my code, then I will use them unless
said code is not compatible with the Global Exception Handlers! (such as
asynchronous methods).

The code should never be more complex then it needs to be, but it should
especially not be less complex then it needs to be. Using UE as the primary
mechanism to deal with unanticipated problems make it far more complex (in
my opinion).
(In other words is the class half empty or is it really half full?)

I would argue that the glass is twice as large as it needs to be. :)
I agree, pushing the recovery off to the program being launched, which can
easily be forgotten, opens the app up to very subtle, hard to
reproduce/fix problems. Where as a Global Exception Handlers helps ensures
that someone is handling the exception, whether its the launchee or the
launcher. I'm saying "helps ensures" as I've seen where exceptions have
been missed by even a Global Exception Handlers, such as asynchronous
methods. In other words if anything I'm suggesting you really should have
both! Especially if you are using a launcher!

You miss my point...there should always be a UE handler somewhere, if only
for the reason of logging the exception, and asking the user to continue or
abort. But that is simply a last-ditch effort! How would the user even know
if it was safe to continue? The user has no context to evaluate the system.
Conversely, how would the system know that it wasn't? What should be done in
a UE, and would that always be the correct decision in all cases?
It appears (based on your earlier reference to UE) you are only referring
strictly to AppDomain.UnhandledException, where as I am referring to all
three Global Exception Handlers; AppDomain.UnhandledException,
HttpApplication.Error and Application.ThreadException. Plus I am
suggesting that you should use the one that is appropriate, I find
AppDomain.UnhandledException not to be very useful in most Windows Forms
or Web Forms applications. Instead I find Application.ThreadException to
be extremely useful in Windows Forms applications, and
HttpApplication.Error & Page.Error (TemplateControl.Error) to be extremely
useful in Web Forms applications.

These are all useful for various purposes.

In other words: Pick the right tool for the right job!
We can agree on this!


BTW: If you re-read my statements I never stated that using Global
Exception Handlers are an absolute, I simply stated that is what I tended
(normally) used! I hope you agree that not using should not be an absolute
either!

I never argued against using them at all. I argued against using them as the
primary mechanism for dealing with exceptions.

Hope this helps,
Dave
 
J

Jay B. Harlow [MVP - Outlook]

David,
As was I. BTW, there is only one true UE, the AppDomain.UE - the others
are actually events generated by different libraries that wrap execution
threads
AppDomain.UnhandledException is an event also!
You miss my point...there should always be a UE handler somewhere, if only
for the reason of logging the exception, and asking the user to continue
or abort.
That is exactly my point! I'm really not sure why you think I stated
otherwise...

Hope this helps
Jay
 
C

craig

2 questions:

1) What is a launcher?

2) What do you mean by an exception being swallowed?

My app is a windows forms app and I am really struggling to find a
consistent, logical strategy for implementing exception management. Looks
like I am going to have to invest some time studying the info in this
thread.

Are the two of you suggesting that unhandled exceptions should be allowed to
propagate up to a global exception handler which logs the exception and
closes down the app? This would mean that the following code should never
be used:

try
{
//program logic
}
catch(Exception ex)
{
//response to exception
}

I have used code like this all over in my app.
 
D

David Levine

craig said:
2 questions:

1) What is a launcher?

A managed application that launches another windows (.net) application. It
could do so in a separate process, or load and execute it in another
appdomain within the same process; the latter is what I have been referring
to in the preceding dicussion.
2) What do you mean by an exception being swallowed?
The following demonstrates swallowing an exception...
try
{
//evil code here
}
catch(Exception ex)
{
// trace, log, or ignore it. But it is not rethrown from within this
catch block
}

In this example, all exceptions will be caught in this catch block. Because
the exception is not rethrown then the exception ends here.
try
{
}
catch(NullReferenceException n)
{
}

In this case only a single exception type is caught, dealt with, and it ends
here. All other exception types are not caught so they continue to propagate
up the callstack.

{
}
catch(Exception ex)
{
if ( canHandleIt )
{ //handle and recover here
}
else
throw new Exception("I fell down and can't get up.",ex);
}

In this example, if the exception can be handled it is dealt with and ended
here, otherwise it is rethrown so that an exception continues to propagate
up the callstack. I used a new exception object, using the original
exception as the inner exception. I prefer this technique as you can add
valuable context information so that when it is eventually displayed to the
user s/he will get a better sense of what went wrong and can more
effectively deal with it. This referred to as the catch-wrap-throw
technique.

A variation on this is to use
catch(Exception ex)
{
// do some processing
throw;
}

A naked throw tells the CLR to continue to propagate the original exception
unchanged, including leaving the original stack trace alone (unfortunately,
in current version this actually resets it to the LOC where the throw is,
but that is supposed to be fixed).

I recommend against the following...
catch(Exception ex)
{
throw ex;
}
and
catch(Exception ex)
{
throw new SomeException("Some message")
}

The problem with the 1st is that it adds no additional information yet loses
the original stack trace. The 2nd has a similar problem and does not even
preserve the original exception type, so you lose even more data. However,
this form *might* be useful if this is a security sensitive operation and
you want to hide the real exception type.

I prefer the catch-wrap-throw technique as it adds info and does not lose
anything. I often times will throw the same exception type (i.e. I clone the
type) if there is no other type that I can map it to that adds useful
information.
My app is a windows forms app and I am really struggling to find a
consistent, logical strategy for implementing exception management. Looks
like I am going to have to invest some time studying the info in this
thread.

Are the two of you suggesting that unhandled exceptions should be allowed
to propagate up to a global exception handler which logs the exception and
closes down the app? This would mean that the following code should never
be used:

If it is unhandled then by definition it is propagating upwards to a global
handler. If you do not provide one the system will provide a default action.
For UEs that occur on the main thread, or threads that originate from
unmanaged code, the application is terminated. For all other threads the UE
is discarded and you get no other indications that this occurred. The
terminate-or-run policy is currently set by the CLR itself, not the
application.

The app can subscribe to a UE event, and it can take some action within that
event, such as put up a messagebox, log it, etc., but if the system has
determined that the app should terminate the UE handler has no means of
changing that. In other words, if a UE occurs and the system decides it
should terminate the app, then wave bye-bye because you cannot change that
within the UE handler. There's a flag in the event args that indicates if
the app will terminate once the handler has run to completion.

When a UE occurs you can terminate the app on your own (call
Envurinment.Exit()) but you need to be aware that if you do then all other
threads in the system are frozen and never resume - this means that
unfinished finally blocks will not run and will never execute the clean up
code (this is v1.1 behavior - I don't know how this might have changed for
Whidbey).

This is one of the reasons I do not like the global UE strategy - you leave
the basic decision of terminating the app or allowing it to continue to the
system - I believe this ought to be an application policy decision.

You should have a registered handler, if for no other reason that exceptions
do not get "lost" when a UE occurs on a thread which will silently swallow
the exception - that's usually a bug.



try
{
//program logic
}
catch(Exception ex)
{
//response to exception
}

I have used code like this all over in my app.
Like so many other things, it depends...this code may be fine; it depends on
what you want it to do. If the catch handler completely deals with it then
this is fine. If it does not, then if this is the final boundary of the
application you must make a call about whether to allow the app to run or
terminate it. If it is not, then you need some means to propagating the
exception upwards to the final handler in the system.
 
J

Jay B. Harlow [MVP - Outlook]

Craig,
1) What is a launcher?
A launcher is a small program that loads your program & runs it. Useful for
(but not limited to) apps that dynamically update themselves from a web
site, but run locally. The launcher downloads the required assemblies & runs
them locally... The launcher itself may be installed locally, so as to allow
any required permissions needed...

2) What do you mean by an exception being swallowed?
A try/catch or Global Exception Handler caught an exception & never let
other routines that called it or the user know. Depending on the calling &
called routines, this can be either a good thing or bad thing.

In the case of Global Exception Handlers, this can prevent your app from
terminating unexpectedly.

Are the two of you suggesting that unhandled exceptions should be allowed
to propagate up to a global exception handler which logs the exception and
closes down the app?
If all I am doing with the exception is logging it, I am suggesting that
normally I allow a global exception handler log it (mainly to avoid
duplicate try/catch log blocks). If I have other "recovery" that is needed
to occur, then I use a try/catch block to perform the recovery. Depending on
where & what this recovery is I either log the exception, not log the
exception, rethrow the exception, or throw a new exception. Note that global
exception handlers really don't "recover" from exceptions, they can log the
exception & at a high level decide if the app should terminate or keep
going...

Hope this helps
Jay

craig said:
2 questions:

1) What is a launcher?

2) What do you mean by an exception being swallowed?

My app is a windows forms app and I am really struggling to find a
consistent, logical strategy for implementing exception management. Looks
like I am going to have to invest some time studying the info in this
thread.

Are the two of you suggesting that unhandled exceptions should be allowed
to propagate up to a global exception handler which logs the exception and
closes down the app? This would mean that the following code should never
be used:

try
{
//program logic
}
catch(Exception ex)
{
//response to exception
}

I have used code like this all over in my app.
<<snip>>
 

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