.NET 中的未处理异常
2024-04-15 08:42:08

在 .NET 中,如果主线程发生了一个异常而未被try/catch捕获的话则会弹出一个错误对话框,报告该异常的信息:

用户可以点击详细信息按钮,将其中的内容提供给开发者,帮助开发者排查问题。同时允许用户继续运行程序。

未处理异常事件

在 .NET 中,有两个事件可以处理未处理异常:

  1. Application.ThreadException。仅适用于 WinForms 程序,只能处理主线程异常,且程序可以继续运行。
  2. AppDomain.UnhandledException


两者的区别很明显,ThreadException 是 WinForms 程序特定的,只处理 UI 线程的异常,并且程序允许不退出。
而 UnhandledException 是真正意义上的未处理异常处理事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
internal static class Program
{
[STAThread]
static void Main()
{
Application.ThreadException += ApplicationOnThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Application.Run(new Form1());
}

private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show("UnhandledException");
}

private static void ApplicationOnThreadException(object sender, ThreadExceptionEventArgs e)
{
MessageBox.Show("ThreadException");
}
}

设置异常处理模式

通过 Application.SetUnhandledExceptionMode 方法指定异常处理模式。
参数是一个 UnhandledExceptionMode 类型。

说明
Automatic 将所有异常都传送到 ThreadException 处理程序,除非应用程序的配置文件指定了其他位置。
ThrowException 从不将异常传送到 ThreadException 处理程序。 忽略应用程序配置文件。
CatchException 始终将异常传送到 ThreadException 处理程序。 忽略应用程序配置文件。


简单说,处理模式就是决定主线程异常要不要交给 UnhandledException 事件处理

通过配置文件指定异常处理模式

这些选项都提到了应用程序配置文件,用文件是这样配置的:

1
2
3
4
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.windows.forms jitDebugging="true"/>
</configuration>

这个配置等同于在程序中使用:

1
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

不过这个配置文件选项只有在没有指定 ThreadException 处理程序时有效
看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void Main()
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.Automatic); // Automatic 是默认值
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Application.Run(new Form1());
}

private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MessageBox.Show("UnhandledException");
}

根据 Automatic 的作用,未处理异常应该在 ThreadException 中处理,但由于配置了<system.windows.forms jitDebugging="true"/>,导致异常交给了 UnhandledException 来处理。
如果想忽略配置文件的设置,应该将模式改为UnhandledExceptionMode.CatchException
对于这个文件配置,了解一下即可,几乎不会用到。

legacyUnhandledExceptionPolicy

legacyUnhandledExceptionPolicy配置可以防止工作线程未处理异常后强制退出。

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="true" />
</runtime>
</configuration>

总结

  • ThreadException 这个名字有点迷惑,实际它就是主线程异常处理事件。
  • 对于异常处理模式,Automatic 是默认值,不需要显示指定。CatchException 和 Automatic 作用相同,只是忽略配置文件,而配置文件基本不会用到,所以当我们要显示指定处理模式时,基本就是设置为 ThrowException。

相关阅读

Application.ThreadException 和 AppDomain.CurrentDomain.UnhandledException 之间有什么区别?
当 UnhandledExceptionMode 设置为 UnhandledExceptionMode.Automatic 时,哪个 .config 元素会影响异常处理?
配置 legacyUnhandledExceptionPolicy 防止后台线程抛出的异常让程序崩溃退出