.NET - What's the best way to implement a "catch all exceptions handler"

I'm wondering what the best way is to have a "if all else fails catch it".

I mean, you're handling as much exceptions as possible in your application, but still there are bound to be bugs, so I need to have something that catches all unhandled exceptions so I can collect information and store them in a database or submit them to a web service.

Does the AppDomain.CurrentDomain.UnhandledException event capture everything? Even if the application is multithreaded?

Side note: Windows Vista exposes native API functions that allow any application to recover itself after a crash... can't think of the name now... but I'd rather not use it, as many of our users are still using Windows XP.

35358 次浏览

In ASP.NET, you use the Application_Error function in the Global.asax file.

In WinForms, you use the MyApplication_UnhandledException in the ApplicationEvents file

Both of these functions are called if an unhandled exception occurs in your code. You can log the exception and present a nice message to the user from these functions.

For WinForms, don't forget to attach to the current Thread's unhandled exception event too (especially if you are using multi threading).

Some links on best practices here and here and here (probably the best exception handling article for .net)

For Winform applications, in addition to AppDomain.CurrentDomain.UnhandledException I also use Application.ThreadException and Application.SetUnhandledExceptionMode (w/ UnhandledExceptionMode.CatchException). This combination seems to catch everything.

On the main thread, you have the following options:

For other threads:

  • Secondary threads have no unhandled-exceptions; use SafeThread
  • Worker threads: (timer, threadpool) there is no safety net at all!

Bear in mind that these events do not handle exceptions, they merely report them to the application--often when it is far too late to do anything useful/sane about them

Logging exceptions is good, but monitoring applications is better ;-)

Caveat: I am the author of the SafeThread article.

There's also a cool thing called ELMAH that will log any ASP.NET errors that occur in a web application. I know you're asking about a Winform App solution, but I felt this could be beneficial to anyone needing this type of thing on a web app. We use it where I work and it's been very helpful in debugging (especially on production servers!)

Here's some features that it has (pulled right off the page):

  • Logging of nearly all unhandled exceptions.
  • A web page to remotely view the entire log of recoded exceptions.
  • A web page to remotely view the full details of any one logged exception.
  • In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrors mode turned off.
  • An e-mail notification of each error at the time it occurs.
  • An RSS feed of the last 15 errors from the log.
  • A number of backing storage implementations for the log, including in-memory, Microsoft SQL Server and several contributed by the community.

You can monitor most exceptions in that handler even in multithreaded apps, but .NET (starting with 2.0) won't allow you to cancel unhandled exceptions unless you enable the 1.1 compatibility mode. When that happens the AppDomain will be shut down no matter what. The best you could do is launch the app in a different AppDomain so that you can handle this exception and create a new AppDomain to restart the app.

I am using the following approach, which works and reduces greatly the amount of code ( yet I am not sure if there is a better way or what the pitfalls of it might be. Whenever you call: I quess the quys giving minuses would be polite enough to clarify their actions ; )

try
{
CallTheCodeThatMightThrowException()
}
catch (Exception ex)
{
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch

And here is the ErrorHandler code : Just to make clear- : objUser - is the object modelling the appusers ( you might get info such as domain name , department , region etc. for logging purposes ILog logger - is the logging object - e.g. the one performing the logging activities StackTrace st - the StackTrace object giving you debugging info for your app

using System;
using log4net; //or another logging platform


namespace GenApp.Utils
{
public class ErrorHandler
{
public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex )
{
if (ex is NullReferenceException)
{
//do stuff for this ex type
} //eof if


if (ex is System.InvalidOperationException)
{
//do stuff for this ex type
} //eof if


if (ex is System.IndexOutOfRangeException)
{
//do stuff for this ex type
} //eof if


if (ex is System.Data.SqlClient.SqlException)
{
//do stuff for this ex type
} //eof if


if (ex is System.FormatException)
{
//do stuff for this ex type
} //eof if


if (ex is Exception)
{
//do stuff for this ex type
} //eof catch


} //eof method


}//eof class
} //eof namesp

I have just played with AppDomain's UnhandledException behavior, (this is the last stage the unhandled exception is registered at)

Yes, after processing the event handlers your application will be terminated and the nasty "... program stopped working dialog" shown.

:) You still can avoid that.

Check out:

class Program
{
void Run()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);


Console.WriteLine("Press enter to exit.");


do
{
(new Thread(delegate()
{
throw new ArgumentException("ha-ha");
})).Start();


} while (Console.ReadLine().Trim().ToLowerInvariant() == "x");




Console.WriteLine("last good-bye");
}


int r = 0;


void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Interlocked.Increment(ref r);
Console.WriteLine("handled. {0}", r);
Console.WriteLine("Terminating " + e.IsTerminating.ToString());


Thread.CurrentThread.IsBackground = true;
Thread.CurrentThread.Name = "Dead thread";


while (true)
Thread.Sleep(TimeSpan.FromHours(1));
//Process.GetCurrentProcess().Kill();
}


static void Main(string[] args)
{
Console.WriteLine("...");
(new Program()).Run();
}
}

P.S. Do handle the unhandled for Application.ThreadException (WinForms) or DispatcherUnhandledException (WPF) at the higher level.

In a manged GUI app, by default, exceptions that originate in the GUI thread are handled by whatever is assigned to the Application.ThreadException.

Exceptions that originate in the other threads are handled by AppDomain.CurrentDomain.UnhandledException.

If you want your GUI thread exceptions to work just like your-non GUI ones, so that they get handled by AppDomain.CurrentDomain.UnhandledException, you can do this:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

An advantage to catching the GUI thread exceptions using ThreadException is that you can give the use the options of letting the app continue. To make sure no config files are overriding default behavior, you can call:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

You are still vulnerable to exceptions from badly behaved native dlls. If a native dll installs its own handler using Win32 SetUnhandledExceptionFilter, it is supposed to save the pointer to the previous filter and call it too. If it doesn't do that, your handler wont' get called.