如何在WinForms应用程序中捕获所有“未处理”的异常?

94

我之前在程序的入口点 Program.cs 中,只是将 try/catch 块放在了 Application.Run 方法的周围。这样在调试模式下可以很好地捕获所有异常,但是当我在非调试模式下运行程序时,异常就不再被处理了。我会看到未处理异常的对话框。

我不想让这种情况发生。我希望无论在何种模式下运行程序,都能够捕获所有异常。程序中有多个线程,最好能够通过同一个处理程序来捕获所有线程的异常;我希望将异常记录在数据库中。请问有什么建议吗?


以下是有关编程的内容的翻译(仅返回翻译文本):相关但不重复的链接:https://dev59.com/93VC5IYBdhLWcg3w51hv - JumpingJezza
4个回答

130

看一下来自ThreadException文档的示例:

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

在调试时,您可能还想避免捕获异常,因为这样可以更轻松地进行调试。这有点像是一种hack操作,但您可以使用以下代码将其包装起来:

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

为了在调试时避免捕获异常。

编辑:一种替代方法,用于检查应用程序是否在调试器中运行,该方法比检查文件名更简洁。

(参见moltenform、Kiquenet和Doug的评论)

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

这样做避免了使用与 vshost.exe 不同的调试器所带来的问题。


1
我创建了一个后台工作器,在dowork事件处理程序中故意引发了一个空引用异常。然而,尽管设置了以下内容,它并没有被AppDomain.CurrentDomain.UnhandledException捕获: Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); - Isaac Bolinger
5
@IsaacB,后台工作器自己捕获异常。您可以在RunWorkerCompleted事件中检查异常,查看RunCompletedEventArgs.Error属性。 - Can Gencer
1
您可以将以下代码放入主窗体的OnLoad事件中,以测试其他线程的异常处理。new Thread(() => { throw new Exception(); }).Start(); - Can Gencer
7
不要使用FriendlyName.EndsWith的技巧,尝试使用更简洁的Debugger.IsAttached。 - moltenform
在我看来,请在你的回答中添加注释使用 Debugger.IsAttached防止在调试时捕获异常 和 _即使处理了 UnhandledException,应用程序也仍然会终止_(参考 by @NazarGrynko) - Kiquenet
显示剩余5条评论

27
在NET 4中,默认情况下不再捕获某些异常,这些异常往往表示可执行文件的(可能致命的)损坏状态,例如AccessViolationException。
尝试在主方法前使用[HandleProcessCorruptedStateExceptions]标记,例如:
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;
  } 

我可以在[HandleProcessCorruptedStateExceptions]标记下同时使用AppDomain.CurrentDomain.UnhandledExceptionApplication.ThreadException吗? - Kiquenet

22

一个好的例子可以在http://www.csharp-examples.net/catching-unhandled-exceptions/找到。 基本上,将您的主要函数更改为:

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

9
您可以使用NBug库来实现此功能。只需进行最小化的设置,如下所示:
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;

你可以开始收集应用程序中所有未处理的错误信息,即使它已经部署到客户端。如果不想使用第三方库,你需要附加到以下事件:
// These two should come before enabling visual styles or running the application
AppDomain.CurrentDomain.UnhandledException += ...
Application.ThreadException += ...
...
Application.Run(new Form1());

1
欢迎。如果您有进一步的问题,请使用NBug项目讨论论坛(http://www.nbusy.com/forum/f11/)或在此处使用[nbug]标签。 - Teoman Soygul
当然,您也可以订阅“常规”事件处理程序到UnhandledException事件。请参阅http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx。 - neo2862
如果我在Win7 + VS10上订阅这些事件,订阅不会运行,而是会出现常规的Windows Vista/7对话框,显示“检查在线解决方案”或“关闭程序”等。但是,如果我不订阅,就会出现常规的通用.NET未处理异常窗口。这在发布版和调试版中都会发生,还尝试设置Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);也没有改变任何东西。 - gideon
@giddy 尝试连接到所有可能的未处理异常事件:UnhandledException、ThreadException(WinForms)、DispatcherUnhandledException(WPF)、UnobservedTaskException(Threading.Tasks)。有可能你错过了其中一个未处理异常源。 - Teoman Soygul
我不知道好的模式和实践。在使用AppDomain.UnhandledException事件时要小心。注意:我被Phillip Haack指出了这个重要的遗漏。另一个常见的错误来源是Application.ThreadException事件。在使用它们时有很多注意事项。 - Kiquenet
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接