Handling unhandled exceptions problem
Asked Answered
S

6

41

I wanted to set some handler for all the unexpected exceptions that I might not have caught inside my code. In Program.Main() I used the following code:

AppDomain.CurrentDomain.UnhandledException
    += new UnhandledExceptionEventHandler(ErrorHandler.HandleException);

But it didn't work as I expected. When I started the application in debugging mode and threw an exception it did call the handler, but afterwards the exception helper in Visual Studio popped up as if the exception occurred without any handling. I tried Application.Exit() inside the handler but it didn't work as well.

What I would like to achieve is that the exception is handled with my handler and then the application closes nicely. Is there any other way to do it or am I using the code above in the wrong way?

Sandblind answered 2/1, 2009 at 9:42 Comment(0)
G
36

It's because you're running it through Visual Studio in Debug mode. If you release and install your app somewhere else, nothing but your global exception handler will be processed.

Gassy answered 2/1, 2009 at 9:45 Comment(0)
R
43

Normally I use something like this to try and catch all unexpected top-level exceptions.

using System;

static class Program
{
  [STAThread]
  static void Main(string[] argv)
  {
    try
    {
      AppDomain.CurrentDomain.UnhandledException += (sender,e)
      => FatalExceptionObject(e.ExceptionObject);

      Application.ThreadException += (sender,e)
      => FatalExceptionHandler.Handle(e.Exception);

      // whatever you need/want here

      Application.Run(new MainWindow());
    }
    catch (Exception huh)
    {
      FatalExceptionHandler.Handle(huh);
    }
  }

  static void FatalExceptionObject(object exceptionObject) {
    var huh = exceptionObject as Exception;
    if (huh == null) {
      huh = new NotSupportedException(
        "Unhandled exception doesn't derive from System.Exception: "
         + exceptionObject.ToString()
      );
    }
    FatalExceptionHandler.Handle(huh);
  }
}

Maybe it is something you find helpful too? This main code routes all three ways of catching unexpected top-level exceptions through one method call. All you now need is a static class FatalExceptionHandler that includes your top-level exception handling in its Handle method.

And really, any application developer knows there are really just two things to do there:

  1. Show/log the exception like you see fit
  2. Make sure you exit/kill the application process

If you think item two is strange, remember that we only bother to do this in the first place for really exceptional situations. These things are probably bugs that need changes to your application to be accurately addressed. Any other exception handling - the functional kind - should be lower down inside your actual program code, catching specific kinds of exceptions where this makes sense and handling them there in the way that makes sense. Anything else should bubble up to your FatalExceptionHandler to make itself known and stop the possibly crippled program from working from corrupted state

Dead programs tell no lies... ;-)

Rockyrococo answered 2/1, 2009 at 10:42 Comment(3)
As you can see from this Why is UnhandledExceptionEventArgs.ExceptionObject an object and not an Exception? post, it may be unwise to cast e.ExceptionObject to Exception without a check first, as it will not always be of type Exception... you could end up creating a new Exception here.Azote
Wondered why it was object, but never actually looked into this. I learned something new today. Thanks! Will change answer to try and remedy this.Rockyrococo
Have to disagree here, at least for an interactive application with proper use of try...finally...catch blocks. In this event it is a terrible idea to terminate the application. All an unhandled exception means is that an exception made it back to the main program loop, which is actually pretty normal unless one implements handlers on every event handler everywhere. Even if that is your policy, which isn't very DRY, you'll forget one and cause your app to crash rather than merely show a message.Tameshatamez
G
36

It's because you're running it through Visual Studio in Debug mode. If you release and install your app somewhere else, nothing but your global exception handler will be processed.

Gassy answered 2/1, 2009 at 9:45 Comment(0)
W
10

Note that unhandled exceptions are still pretty fatal; you can only really use this for logging, or maybe some hasty close-down. Neither this nor Application.ThreadException can be used as a global sink for errors.

The better approach is to add proper handling - for example, around your entire Main() logic. Note that even this can't catch a few exceptions, such as errors during form-load (which get particularly nasty - you can catch them with a debugger attached, but not without).

Weig answered 2/1, 2009 at 9:48 Comment(2)
then why not catch them in Main()?Weig
"i know that" was an answer to the "sink for errors" part. i was taught try-catch in main is a thing you should not do. however, i must say honestly that i don't know why it would be a bad approach (of course if i catch all exceptions i can think of inside the code)Sandblind
H
2

Perhaps what you're looking for is Environment.Exit(int errorcode)

Hamburg answered 8/3, 2009 at 19:44 Comment(0)
H
0

That behavior is by design.

But there is a work-around.

Either you call Process.GetCurrentProcess().Kill(); within the handler, or simply do not let the handler end.

Check out the example:

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();
    }
}

This should not be a default sink for exceptions, surely.

But this should be done to report exceptions gracefully.

Horan answered 29/6, 2009 at 7:55 Comment(0)
T
0

Note: if you're application is a Windows Forms application, don't forget to:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

... or else the application will terminate, even if you handle the exceptions with the handlers specified by the other answers.

Tilden answered 3/11, 2022 at 16:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.