March 3, 2021
Hot Topics:

What!? A .NET Application Can Die?

  • By John Robbins
  • Send Email »
  • More Articles »

You have to love .NET. All those nasty crash problems we used to wrestle with in Win32 are now just stories we can tell around the virtual campfire. No longer will our users have to see that ugly crash dialog telling them oh so subtly that we programmers screwed up. Life it .NET is great, isn't it?

The prior paragraph was somewhat of a dream sequence. While .NET does keep us from messing with all the hassles of memory corruptions and such, as we all know, reality is a little different. As we've all seen a million times when learning .NET, your applications can certainly end sadly and abruptly. While that's fine during development, we certainly don't want to have our .NET applications die with an exception in front of our users. Nothing is more embarrassing than a confusing dialog or web page referring to unhandled exceptions.

In this column, I want to discuss how to set exception handlers in your .NET applications so you can apologize to the user for abruptly terminating and also gather more information about the unhandled exception so you can stand a chance of duplicating the problem so you can fix it. Back in the Win32 days, it was relatively easy to make a single function call, SetUnhandledExceptionFilter, so you could set a function that would get called no matter what thread caused an exception in your application. While .NET allows the same type of handling, it's got a twist in that console, WinForms, and ASP.NET applications all set up the exception handlers differently. Additionally, there's different behavior depending on which type of thread caused the exception.

After showing you the thread types, I'll discuss setting exception handling in console applications. Part of that discussion will be a small program that shows you how exceptions are reported depending on which thread type has the unhandled exception. Once past the console applications, I'll turn to WinForms applications. Finally, I'll cover ASP.NET applications.

Thread Types

As how the exception handling gets called is dependent on which thread type is causing the exception, its important that I list them. That way you'll see the ramifications when running the ConsoleException program later in the column.

  • Main Thread
    The initial thread of the console or GUI application.
  • Manual Thread
    Threads created with System.Threading.Thread.
  • Pool Thread
    Threads created with System.Threading.ThreadPool or System.Threading.Timer. Used quite a bit in the CLR
  • Finalizer Thread
    The garbage collector thread
  • Unmanaged Thread
    Win32 threads not created by the CLR.

Unhandled Exceptions in Console Applications

The .NET documentation is quite straight forward in pointing out that to handle exceptions for an Application Domain you'll set a System.UnhandledExceptionEventHandler delegate to the AppDomain.CurrentDomain.UnhandledException event like the following:

using System;

namespace SimpleEX
  class Class1
    static void Main ( )
      AppDomain.CurrentDomain.UnhandledException += 
              new UnhandledExceptionEventHandler(SimpleHandler);
      String x = null ;
      Console.WriteLine ( x.ToString ( ) ) ;     
    static void SimpleHandler(Object                      Sender,
                              UnhandledExceptionEventArgs Args  )
      Exception e = (Exception)Args.ExceptionObject ;
      Console.WriteLine ( "Caught : " + e.Message ) ;

While setting your exception handler looks relatively straight forward, there's a couple of key "gotchas" that you need to keep in mind. The first is if you are creating other Application Domains in your code, you can only set the exception handler when executing in an Application Domain. You can't set exception handlers from outside the Application Domain.

The second issue is related to the threads I discussed earlier. If the exception occurs in the main thread, your application will terminate after your exception handler returns. However, if the exception occurs in any other thread, that particular thread will terminate, but the main thread will continue to execute as if nothing happened.

When I first ran across the fact that the main thread continues to execute, I was a little perplexed as I would have thought that the application terminated on any thread. .NET is quite a bit different from Win32, and here's yet another example of where bringing assumptions proves you wrong. If you want to treat any exception as fatal, no matter which thread it comes from, you can call the Environment.Exit method, which will end your application from inside your exception handler.

Inside your exception handler, the UnhandledExceptionEventArgs parameter has a read only property, IsTerminating, which when true, reports that the application is ending. As you'd expect you'll only see the IsTerminating set to true when the main thread has the exception. If the value is false, the thread is terminating, but not the application.

To show you exceptions in each of the managed threads, I whipped together ConsoleExceptions. This program does nothing more than let you easily play with setting an exception handler and dictating which thread type where you want to throw an exception. The command line options are as follows:

Command line options y|n m|t|p|g
You must specify if you want the error handler and which error
  y = install unhandled error handler
  n = use default error handler

  m = main thread
  t = managed thread
  p = pool thread
  g = garbage collector

If you'd like to see what happens when you have an error handler and an exception occurs in the garbage collector thread, the command line is "y g". The main thread has a finallyblock as well as a call to Console.WriteLine to tell you when the application ends. I'd encourage you to play with the various threads and how exceptions are handled in them so you'll be able to properly handle unhandled exceptions occur in your applications.

Page 1 of 2

This article was originally published on March 13, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date