在 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 操作
};
<pre class="brush:php;toolbar:false;">// 启动你的主逻辑
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("发生错误,请重启应用。");
};
<pre class="brush:php;toolbar:false;">// 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 三者,才能覆盖绝大多数托管异常场景。
