How can I make something that catches all 'unhandled' exceptions in a WinForms application?
Asked Answered
G

4

94

Up until now, I just put a try/catch block around the Application.Run in the Program.cs entry point to the program. This catches all exceptions well enough in Debug mode, but when I run the program without the debug mode, exceptions don't get handled anymore. I get the unhandled exception box.

I don't want this to happen. I want all exceptions to be caught when running in non-debug mode. The program has multiple threads and preferably all exceptions from them get caught by the same handler; I want to log exceptions in the DB. Does anyone have any advice in how to do this?

Giguere answered 23/4, 2011 at 6:2 Comment(1)
related, but not duplicate: https://mcmap.net/q/225395/-can-you-catch-a-native-exception-in-c-code/345659Precambrian
C
132

Take a look at the example from the ThreadException documentation:

public static void Main(string[] args)
{
   // Add the event handler for handling UI thread exceptions to the event.
    Application.ThreadException += new     
  ThreadExceptionEventHandler(ErrorHandlerForm.Form1_UIThreadException);

  // Set the unhandled exception mode to force all Windows Forms errors
  // to go through our handler.
  Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

  // Add the event handler for handling non-UI thread exceptions to the event. 
  AppDomain.CurrentDomain.UnhandledException += new       
  UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

You might also want to not catch exceptions when debugging, as this makes it easier to debug. It is somewhat of a hack, but for that you can wrap the above code around with

 if (!AppDomain.CurrentDomain.FriendlyName.EndsWith("vshost.exe")) { ... }

To prevent catching the exceptions when debugging.

EDIT: An alternate way to check for your application running inside a debugger that feels cleaner than checking a filename.

(see comments by moltenform, Kiquenet and Doug)

if(!System.Diagnostics.Debugger.IsAttached) { ... }

This avoids the problem of using a different debugger than vshost.exe.

Cyrille answered 23/4, 2011 at 7:4 Comment(10)
I made a background worker, and in the dowork event handler I intentially caused a null reference exception. However it didn't get caught by the AppDomain.CurrentDomain.UnhandledException in spite of setting these: Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);Giguere
@IsaacB, background worker catches exceptions itself. You can check the exception in the RunWorkerCompleted even, looking at the RunCompletedEventArgs.Error property.Cyrille
You can test the exception handling for additional threads by putting this into the OnLoad of your main form. new Thread(() => { throw new Exception(); }).Start();Cyrille
Unfortunately handling UnhandledException will not stop application from terminating :(Myeshamyhre
Instead of the FriendlyName.EndsWith hack, try Debugger.IsAttached which is cleaner.Sherrillsherrington
IMHO, add notes in your answer, use Debugger.IsAttached for prevent catching the exceptions when debugging and handling UnhandledException will not stop application from terminating ( reference by @NazarGrynko)Mackinnon
Can I combine AppDomain.CurrentDomain.UnhandledException and Application.ThreadException with [HandleProcessCorruptedStateExceptions] tag ? https://mcmap.net/q/223529/-how-can-i-make-something-that-catches-all-39-unhandled-39-exceptions-in-a-winforms-application CLR Inside Out - Handling Corrupted State Exceptions certain exceptions are no longer caught by default; these tend to be exceptions that indicate a (possibly fatal) corrupted state of the executable, such as an AccessViolationException.Mackinnon
In order to not catch exceptions while debugging, you can check System.Diagnostics.Debugger.IsAttached property value. It's more elegant than checking the running executable name.Fischer
There's also usually the preprocessor variable DEBUG, for readers in the future.Inkhorn
@person27 re: DEBUG using that conditional protects you from running this in a DEBUG compile. The recommended techniques let you catch the otherwise unhandled exceptions regardless of DEBUG or RELEASE compile, but protect from trying to double catch them in your code AND in the debugger code.Hachman
S
27

In NET 4, certain exceptions are no longer caught by default; these tend to be exceptions that indicate a (possibly fatal) corrupted state of the executable, such as an AccessViolationException.

Try using the [HandleProcessCorruptedStateExceptions] tag in front of your main method, e.g.

using System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions

[HandleProcessCorruptedStateExceptions]
public static int Main()
{
    try
    {
        // Catch any exceptions leaking out of the program
        CallMainProgramLoop();
    }
    catch (Exception e) // We could be catching anything here
    {
        System.Console.WriteLine(e.Message);
        return 1;
    }
    return 0;
  } 
Soubriquet answered 23/4, 2011 at 8:26 Comment(1)
Can I use AppDomain.CurrentDomain.UnhandledException and Application.ThreadException too with [HandleProcessCorruptedStateExceptions] tag ?Mackinnon
U
23

A nice example can be found at http://www.csharp-examples.net/catching-unhandled-exceptions/ Basically, change your main to:

static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Application.Run(new Form1());
    }

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        MessageBox.Show(e.Exception.Message, "Unhandled Thread Exception");
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        MessageBox.Show((e.ExceptionObject as Exception).Message, "Unhandled UI Exception");
    }
Unconscious answered 12/11, 2015 at 11:42 Comment(0)
D
9

You can use NBug library for that. With minimal setup like this:

NBug.Settings.Destination1 = "Type=Mail;[email protected];[email protected];SmtpServer=smtp.mycompany.com;";
AppDomain.CurrentDomain.UnhandledException += NBug.Handler.UnhandledException;
Application.ThreadException += NBug.Handler.ThreadException;

You can start collecting information on all unhandled bugs in your application, even when it's deployed to the clients. If you don't want to use a 3rd party library, you should attach to below events:

// These two should come before enabling visual styles or running the application
AppDomain.CurrentDomain.UnhandledException += ...
Application.ThreadException += ...
...
Application.Run(new Form1());
Deccan answered 23/4, 2011 at 6:4 Comment(10)
You're welcome. Use the NBug project discussion forum if you have further questions (nbusy.com/forum/f11) or use the [nbug] tag here.Deccan
Of course, you can also subscribe a "regular" event handler to the UnhandledException event. See msdn.microsoft.com/en-us/library/…Ender
Guys on Win7 + VS10, if I subscribe to these events the subscription doesn't run, instead the regular Windows Vista/7 dialog shows up Check Online for a Solution Or Close the Program.. etc. But if I do NOT subscribe, I get the regular generic .NET Unhandled Exception Window. This happens on Both Release and Debug builds, also tried setting Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); Doesn't change anything.Lure
@giddy, after handling the exceptions you should exit the application with Environment.Exit(1); if you don't want the error window displayed.Deccan
@Teo thanks for your reply. I want my own error form to show up and then I want the app to exit. But the event subscription never runs, it just shows the generic Win Vista/7 dialog when it encounters exceptions. But if I don't subscribe the generic .NET unhandled exception dialog appears!Lure
@giddy Try hooking up to all possible unhandled exception events: UnhandledException, ThreadException (WinForms), DispatcherUnhandledException (WPF), UnobservedTaskException (Threading.Tasks). It is possible that you've missed one of these unhandled exception sources.Deccan
I'm on winforms, and subscribed to both Unhandled and Thread Exception.Lure
Try something like this one: Application.ThreadException += (sender, e) => { new MyErrorForm(); Environment.Exit(0); }; note that I changed Environment.Exit(1); to Environment.Exit(0); which should prevent Vista/7 dialog.Deccan
GitHub is the new source repo. Send pull requests there: github.com/soygul/NBugMackinnon
I dont know the good patterns and practices. Be careful when using the AppDomain.UnhandledException event Revision note: I was pointed by Phillip Haack of this important omission. Other common source of mistakes is the Application.ThreadException event. There are lots of caveats when using them,Mackinnon

© 2022 - 2024 — McMap. All rights reserved.