views:

3309

answers:

9

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.

+11  A: 

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

In WinForms, you use the MyApplication_UnhandledException in the ApplicationEvents file

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

amdfan
+4  A: 

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)

Bogdan Maxim
please link to a reference to the "current Thread's unhandled exception event", I was under the impression that there was no such thing!
Steven A. Lowe
+9  A: 

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.

Bob Nadler
everything except exceptions on secondary threads, timer threads, and threadpool threads!
Steven A. Lowe
I agree that if "handling exception" is understood as actually do something to recover from it, AppDomain's "UnhandledException" is not sufficient. However, the original question seemed to be geared only towards error reporting and not about recovering. For that purpose alone it would be sufficient.
Christian.K
@Steven: If the thread pool job is launched via BeginInvoke any exception from that thread is marshalled to the calling thread when EndInvoke is called.
Brian Rasmussen
+3  A: 

AppDomain.CurrentDomain.UnhandledException reports exceptions on the main thread of a console or service application

Application.ThreadException reports exceptions on the main thread of a WinForms application

Global.asax's Application_Error function is for web applications

ah but for threads, use SafeThread; there is no unhandled-exceptions event for secondary threads - and for 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 CALM and the SafeThread article

Steven A. Lowe
A: 

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.
Rorschach
ELMAH is cool (we use a branch of it) but I think he asked about non-web apps..
Jeff Atwood
+2  A: 

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.

jezell
A: 

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
YordanGeorgiev
This will only catch exceptions on the current thread. I.e. it is of little use if CallTheCodeThatMightThrowException creates or uses other threads.
Brian Rasmussen
+4  A: 

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.

modosansreves
Handling the DispatcherUnhandledException solved my problems, forgot to update the question though. Thnx for the answer
TimothyP
I guess it is fair to mention, that the code above leaves every failed thread lying around forever. I.e. sooner or later you will have loads of zombie threads hanging around. Personally I don't find that very useful.
Brian Rasmussen
I agree. That produced zombies. In sorrow I accept this alternative to killing my application immediately after the handler exits. After all, before letting the dead thread sleep forever, you can send a report to your server, let the user finish tasks at hand and exit (or restart application) gracefully.
modosansreves
But if that is all you want to do, you don't need to let the thread sleep forever. That is what the UnhandledException event is there for.
Brian Rasmussen
In case the handler exits, the runtime will show a message "XXX application has stopped working.". This is by design since .Net 2.0. Yet there is an option to make CLR work in .Net 1.1 error-handling compatibility mode (by adding a line to the manifest). Personally I do not like that design and thus I prefer my code to send error report. This is the reason of making that thread background. The unwanted message won't appear if the application kills itself (this is done intentionally after releasing resources etc).
modosansreves
Let the app die? Bull, that's a terrible thing to ever do to your user. And your program - use the normal channels to clean up. and What if you are writing a webserver? Can you only write toys in .net? Any serious app will need to have a watchdog to restart it anyway. Pain in the arse b/c the main program has to be always saving reload/state/where-am-i info.
FastAl
Em... The app is going to die anyway. Either by your own will or with the help of CLR. Btw, a nice idea to keep a watchdog aside.
modosansreves
A: 

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.

Corey Trager