C#全局异常捕获需组合使用AppDomain.UnhandledException(非UI线程)、Application.ThreadException(WinForms UI线程)、DispatcherUnhandledException(WPF UI线程)和TaskScheduler.UnobservedTaskException(异步任务),缺一不可。

在 C# 中捕获全局未处理异常,主要靠 AppDomain.CurrentDomain.UnhandledException 事件,但它**只适用于非 UI 线程和主线程中未被 try-catch 捕获的异常**,且**无法阻止程序退出**(仅能记录日志、做善后)。它不是“万能兜底”,尤其对 WinForms/WPF 的 UI 线程异常需配合其他机制。
AppDomain.UnhandledException 基本用法
该事件在异常未被任何 catch 捕获、即将导致进程终止前触发。注册一次即可,通常放在 Main() 开头或应用启动处:
示例:
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
var ex = e.ExceptionObject as Exception;
Console.WriteLine($"全局异常:{ex?.Message}");
// 记录日志、保存状态、弹提示(谨慎)、发送告警等
// ⚠️ 注意:e.IsTerminating 为 true,此时不应调用复杂逻辑或 UI 操作
};
// 启动你的主逻辑
Application.Run(new MainForm()); // WinForms 示例
}
它不能捕获哪些异常?
以下情况 不会触发 此事件:
- UI 线程中抛出的异常(如 WinForms 的 Button.Click 内未捕获异常)——这类由 Windows 消息循环拦截,需用
Application.ThreadException(WinForms)或DispatcherUnhandledException(WPF) - Task 异常未 await 或未 .Wait()/.Result —— 默认不传播到主线程,需监听
TaskScheduler.UnobservedTaskException - 已用 try-catch 捕获并吞掉的异常
- StackOverflowException、OutOfMemoryException(部分情况下)等严重运行时异常
WinForms / WPF 必须补充的监听
为了真正“全局”,必须组合使用:
-
WinForms:注册
Application.ThreadException处理 UI 线程异常 -
WPF:在 App.xaml.cs 中订阅
Application.DispatcherUnhandledException -
所有托管线程:加上
TaskScheduler.UnobservedTaskException防止遗漏异步异常
WinForms 完整示例片段:
static void Main()
{
// 1. UI 线程异常(关键!)
Application.ThreadException += (s, e) =>
{
LogError("UI线程异常", e.Exception);
MessageBox.Show("发生错误,请重启应用。");
};
// 2. 其他线程 + 主线程非 UI 异常
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
LogError("未处理异常", e.ExceptionObject as Exception);
};
// 3. 异步任务异常(未观察到的)
TaskScheduler.UnobservedTaskException += (s, e) =>
{
e.SetObserved(); // 标记为已处理,避免后续终止
LogError("未观察到的任务异常", e.Exception);
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());}
注意事项与最佳实践
这些事件是“最后机会”,但有严格限制:
- 不要在其中执行耗时操作(如写大文件、网络请求)——进程可能随时终止
- 避免调用 UI 控件(如 MessageBox 在非 UI 线程弹窗会失败),如需提示,用
BeginInvoke或检查线程上下文 - 记录日志建议用轻量方式(如写入本地文本、EventLog),并确保路径可写
-
e.IsTerminating为 true 时,禁止 throw 新异常或调用 Environment.Exit() - .NET Core/.NET 5+ 中
AppDomain已弱化,推荐优先用HostBuilder的UseExceptionHandler(ASP.NET)或Try-Catch in Main+ 全局日志中间件
基本上就这些。AppDomain.UnhandledException 是重要一环,但不是全部——搭配 ThreadException、DispatcherUnhandledException 和 TaskScheduler 三者,才能覆盖绝大多数托管异常场景。









