Handling top-level exceptions

R

Robert Rotstein

It appears that exception handling at the top-most level of a C#
program, in the static void Main() method, differs depending on whether
the program is run in debug mode or not. That is, code such as the
following

try
{
Application.Run(new something());
}
catch
{
...
}
finally
{
...
}

invokes the catch{} handler (if an exception is thrown somewhere and is
not caught elsewhere) and the finally{} handler only when the program is
run in debug mode. At least, that is the behavior that I have observed.
How do you get the catch{} and finally{} handlers to be invoked
unconditionally, regardless of how the program has been launched?
 
D

David Levine

Without knowing how you are testing this it is impossible to determine
exactly why your results are skewed.

Given the snippet you show below, the catch handler will catch everything
that is thrown on the main thread, even if the exception is not
CLS-compliant (e.g. a library somewhere throws an integer rather then an
exception object). Also, since the catch block is a 'naked' catch, there is
no exception object associated with it, so you will be unable to examine the
cause of the exception.

It is important that you know which exceptions will not be caught;
exceptions thrown on any other thread will not be delivered to the catch
block. Examples of this include exceptions that occur on a threadpool
thread, a thread that you created manually, and also exceptions that occur
in finalizers, as the GC runs finalizers on a separate thread.

Exceptions thrown on the main thread and which are handled somewhere else
(i.e. further down the call stack another piece of code catches the
exception and handles/swallows it) will not be delivered to this catch
block.

Also, the catch handler will never be called unconditionally...it will only
be executed if an exception occurs on that thread which is not handled
somewhere else.

The finally block will always execute unconditionally, with one
exception....if the application terminates abnormally the finally block will
not run. For example, if some other piece of code calls Environment.Exit.
Under normal circumstances execution returns from the call to
Application.Run(), and from there execution always continues at the finally
block (or the catch block).

None of these behaviors depends on the type of build...debug versus release.
There are some differences that may show up in edge cases but which are not
likely to effect the code snippet you show below.

For example, the statement
Application.Run(new something());
causes an object of type 'something' to be created, but the reference is not
assigned to a local variable, so the shortest lifetime possible is scoped to
the method call to Run(). When running under the debugger the debugger
typically will report the reference as live until the end of the Main's
method body instead of the call to Run()...this makes it easier to debug as
you can examine the object even after the method call has been completed. In
the release build the object's lifetime may end after the call to Run
returns. If the object has a finalizer then in a release build it could be
run before the finally block that follows the Run(), or in a debug build it
may get called after the finally block runs.
 
R

Robert Rotstein

I am running a debug build in both cases. If I run it with F5 (start),
then -- since the program is deliberately constructed to throw an
exception (this is a homework program) -- both the catch{} and the
finally{} are invoked. If the program is started with ctrl-F5 (start
without debugging), neither is reached; instead, a default system
handler is invoked, displaying an appropriate error message.

You can see a description of the homework assignment at
http://www.rollthunder.com/Harvard/homework/index.htm. This is the
second homework assignment. Note that the last sentence of the item no.
8 states: "Hint: Make sure you test the client program outside the
debugger as well as inside, the top-level exception behavior is
different.".
 
D

David Levine

The way you phrased your statement made it appear you were referring to a
difference in the debug versus release builds - instead, you are seeing a
difference when running under a debugger versus when running from a command
line.

The different results you are seeing are not due to differences in the
underlying runtime - it behaves as I described. The difference is due
entirely to the way that the Windows.Forms class is written. That class
catches and deals with all exceptions itself that are not handled by the
event handlers it invokes. If you want to get notified when an exception has
occured that is caught by that handler you need to subscribe to the
Application.ThreadException event. If you do not then you will get the
message box and then your app will terminate. All other unhandled exceptions
are delivered to the AppDomain.UnhandledException handler (except for
asp.net exceptions). Personally, I believe they screwed up when the Forms
class was written did this - it would have been far less confusing to have a
single UE handler for all types of UEs (the AppDomain.UE). There is now a
completely artificial distinction between UEs and a code path that adds no
value IMO.

Anyway, you only get this behavior with the Windows.Forms class; there is
obviously some interaction with the debugger that causes the Forms class to
alter its runtime behavior wrt how it handles a UE.

If you create a simple console app you will see the "real" behavior of the
runtime. For example...

class Class1
{
static void Main(string[] args)
{
try
{
Trace.WriteLine("In Main try block.");
new Class1().TestException();
}
catch
{
Trace.WriteLine("Exception caught in Main.");
}
finally
{
Trace.WriteLine("In Main finally block.");
}
}

public void TestException()
{
throw new Exception("This is an uncaught exception.");
}
}

Regardless of how you run it the finally block will always execute, and the
catch block executes if an exception is thrown.
 

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