我怎样才能在一个 WinForms 应用程序中创建一个能够捕获所有“未处理”异常的程序呢?

到目前为止,我只是在程序的 Program.cs入口点的 Application.Run周围放了一个 try/catch 块。这在调试模式下能够很好地捕获所有异常,但是当我不使用调试模式运行程序时,就不再处理异常了。我得到了未处理的异常框。

我不想这样。我希望在非调试模式下运行时捕获所有异常。该程序有多个线程,最好是由同一个处理程序捕获来自它们的所有异常; 我想在数据库中记录异常。有人对如何做这件事有什么建议吗?

69085 次浏览

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

NBug.Settings.Destination1 = "Type=Mail;From=me@mycompany.com;To=bugtracker@mycompany.com;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());

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.

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

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