C#怎么捕获全局异常 C# AppDomain.CurrentDomain.UnhandledException处理方法

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 已弱化,推荐优先用 HostBuilderUseExceptionHandler(ASP.NET)或 Try-Catch in Main + 全局日志中间件

基本上就这些。AppDomain.UnhandledException 是重要一环,但不是全部——搭配 ThreadException、DispatcherUnhandledException 和 TaskScheduler 三者,才能覆盖绝大多数托管异常场景。