WPF的三个全局异常捕捉

1. UI线程未捕获异常处理事件(DispatcherUnhandledException)

  • 作用和触发时机
    • 在WPF应用中,UI线程负责处理界面的交互、渲染等诸多操作。当在UI线程上发生了异常,并且这个异常没有被代码中常规的 try-catch 块等方式捕获处理时,就会触发 DispatcherUnhandledException 事件。例如,在某个按钮的点击事件处理程序中(该处理程序运行在UI线程上),如果执行了一段可能出错的代码,像访问一个空引用的对象属性或者进行非法的类型转换等操作且没有提前捕获异常,那么这个事件就会被触发。
  • 使用方式及示例代码
public partial class App : Application
{
    private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        // 可以在这里记录异常信息,比如写入日志文件
        Console.WriteLine($"UI线程发生未捕获异常: {e.Exception.Message}");
        // 设置为已处理,避免程序默认的异常弹出对话框导致程序终止(可根据需求决定是否这样做)
        e.Handled = true; 
    }
}

在上述示例中,定义在 App 类(通常是WPF应用的启动类,继承自 Application)里的 App_DispatcherUnhandledException 方法就是用来处理UI线程未捕获异常的。可以根据实际需求在这个方法中进行更复杂的操作,比如弹出一个友好的提示框告知用户出现了错误、将详细异常信息发送到远程服务器用于分析等,最后通过设置 e.Handledtrue 来告知WPF框架这个异常已经被“处理”了,从而避免应用程序直接崩溃并弹出默认的异常对话框。

2. Task线程内未捕获异常处理事件(TaskScheduler_UnobservedTaskException)

  • 作用和触发时机
    • 当在使用 Task 来进行异步操作时(例如通过 Task.Run 启动一个在后台线程执行的任务等),如果在这个 Task 所代表的异步操作中发生了异常,并且这个异常没有被 Task 内部的 try-catch 等机制捕获,同时在后续代码中也没有主动去观察(处理)这个异常(例如没有调用 TaskWaitResult 等会引发异常传播的方法,或者没有注册异常处理回调等),就会触发 TaskScheduler.UnobservedTaskException 事件。比如在一个异步下载文件的 Task 中,如果因为网络问题或者文件权限问题导致出现异常,且没有在 Task 本身的执行逻辑里处理,那么就可能触发该事件。
  • 使用方式及示例代码
public partial class App : Application
{
    private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        // 记录异常相关信息
        Console.WriteLine($"Task线程发生未捕获异常,异常详情如下:");
        foreach (var innerException in e.Exception.InnerExceptions)
        {
            Console.WriteLine(innerException.Message);
        }
        // 设置为已观察,避免CLR(公共语言运行时)终止应用程序(根据实际情况决定是否这样做)
        e.SetObserved();
    }
}

这里的 TaskScheduler_UnobservedTaskException 方法用于处理 Task 线程中的未捕获异常情况。在实际应用中,可以把异常信息发送给开发者用于分析异步任务失败的原因,同时通过 e.SetObserved() 告知运行时这个异常已经被关注了,防止因为未处理的异常积累等情况导致整个应用程序意外终止,因为默认情况下,未观察到的 Task 异常可能会使CLR采取一些比较强硬的措施来终止应用的运行。

3. 非UI线程未捕获异常处理事件(CurrentDomain_UnhandledException)

  • 作用和触发时机
    • 对于那些既不是UI线程也不是通过 Task 等特定异步机制管理的普通线程(比如手动通过 Thread 类创建并启动的线程等),当在这些线程上发生了异常且没有被捕获时,就会触发 AppDomain.CurrentDomain.UnhandledException 事件。例如,创建一个新线程去执行一个长时间运行的数据处理任务,如果在这个数据处理过程中出现了诸如数组越界、内存不足等异常且没有提前处理,就会引发该事件。
  • 使用方式及示例代码
public partial class App : Application
{
    private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        // 输出异常信息
        Console.WriteLine($"非UI线程发生未捕获异常,异常消息: {((Exception)e.ExceptionObject).Message}");
        // 这里的IsTerminating属性可以判断是否会导致应用程序终止
        if (e.IsTerminating)
        {
            Console.WriteLine("该未捕获异常将导致应用程序终止");
        }
    }
}

CurrentDomain_UnhandledException 方法负责应对非UI线程的未捕获异常情况。可以利用这个机会在应用程序彻底崩溃前做一些重要的事情,比如保存当前应用的一些关键状态数据到本地磁盘等,因为一旦触发这个事件且 IsTerminating 属性为 true (通常情况下是这样,除非应用有特殊的宿主环境等可以避免终止),意味着应用程序即将因为这个未处理的异常而结束运行了。

合理利用这三种异常处理事件机制,可以增强WPF应用程序的健壮性,让应用在面对各种可能出现的异常情况时能够有更好的应对策略,尽可能避免因为异常导致的程序意外崩溃以及数据丢失等问题。

posted @ 2024-11-27 17:24  阿遇而已  阅读(357)  评论(0编辑  收藏  举报