• WPF异常处理


    一、WPF中的未捕获异常(UnhandledException)

    首先,我们当然是要求应用程序开发人员,尽可能地在程序可能出现异常的地方都去捕捉异常,使用try…catch的方式。但是总是有一些意外的情况可能会发生,这就导致会出现所谓的“未捕获异常(UnhandledException)”。对于这一类异常,如果我们没有一个合适的策略进行处理,则当其发生的时候,会给用户带来不太好的使用体验。例如下面这样:

    image

    用户看到这个窗口的时候,其实一般只能点击Close the prograrm按钮。也就是说,这种情况下会导致用户无法继续使用这个程序,而且他们还得不到任何具体的消息:到底发生了什么事情了?除非他们去查看Windows的事件日志。(但一般的用户是不太会这个操作的)

    image

    我们可以看到在Windows事件日志中,会有两个具体的事件。首先是一个.NET Runtime的事件:

    image

    然后是一个Application Error的事件:

    image

    通常来说,这样的用户体验有值得改进的地方。我们虽然不能防止异常的产生,但是当意外发生的时候,我们应该要以更好地方式地通知到用户,或者尽可能地不要影响用户当前的操作。

    在WPF这种应用程序中,会有两大类未处理异常:一类是在UI线程抛出来的,例如点击了用户界面上面的某个控件,然后执行某个代码的时候,遇到了异常;另一类是非UI线程跑出来的,例如在一个多线程的程序里面,工作线程的代码遇到了异常。

    对于UI线程的未处理异常,我们可以通过监控下面Application.Current.DispatcherUnhandledException 事件来处理。

    对于非UI线程抛出的未处理异常,我们需要监控另外一个AppDomain.CurrentDomain.UnhandledException事件来处理。

                // 避免多次添加事件处理
                Application.Current.DispatcherUnhandledException -= AppOnDispatcherUnhandledException;
                AppDomain.CurrentDomain.UnhandledException -= CurrentDomainUnhandledException;
    
                Application.Current.DispatcherUnhandledException += AppOnDispatcherUnhandledException;
                AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
    

    1、未捕获UI异常(Application.Current.DispatcherUnhandledException )

            /// <summary>
            /// UI线程抛出全局异常事件处理。
            /// </summary>
            private static void AppOnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
            {
                 MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);//这里通常需要给用户一些较为友好的提示,并且后续可能的操作
    
                e.Handled = true;//使用这一行代码告诉运行时,该异常被处理了,不再作为UnhandledException抛出了。
            }
    

    2、未捕获非UI异常(AppDomain.CurrentDomain.UnhandledException)

            /// <summary>
            /// 非UI线程抛出全局异常事件处理。
            /// </summary>
            private static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
            {
                MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);
            }
    

    令人不解的是,这个事件中没有和前面那个事件一样的e.Handled参数,就是说,虽然这样是可以捕捉到非UI线程的异常,而且也可以进行相应的处理,但是应用程序还是会退出,也就是说这个异常还是被当作是未处理异常继续汇报给Runtime。

    为了改进这一点,我们可以通过修改配置文件来实现。

    二、配置 legacyUnhandledExceptionPolicy

    WPF 和 Windows Forms 都是微软的框架,为了照顾初学者,微软会默认每一个开发者都不会正确地处理异常。于是在异常发生之后,微软 Windows 会假设开发者并不知道如何应对以便让应用程序正常工作,就擅自将应用程序进程结束掉,以便防止应用程序自己内部产生奇怪的状态和错误,避免对系统环境造成不可逆的严重后果。

    能够写出异常处理代码的开发者,微软会默认他们懂了异常处理。

    写出了监听 Dispatcher.UnhandledException 事件的开发者,微软会认为他们已经学会了如何在 UI 线程中处理异常。于是允许开发者设置 e.Handled = true 来标记异常已被正确处理,程序可以不用退出了。

    还有一个事件 Appdomain.CurrentDomain.UnhandledException,然而这个事件却并不允许开发者标记 e.Handled = true。因为微软认为,应用程序域中所有的线程发生异常都会进入这个事件中,大多数开发者都不明白这些线程这些异常是怎么回事,所以不认为这些开发者具备正确处理这些异常的能力。比如 WPF 的触摸模块发生了异常,开发者知道如何恢复吗?并不知道,还不如结束掉程序然后重启呢!

    在这个事件中,有一个属性 IsTerminating 指示是否应用程序正因为这次异常准备退出,不过开发者并不能拿这个属性做些什么。

    但还是要照顾更高级的开发者的,于是祭出新的配置——legacyUnhandledExceptionPolicy

    app.config 文件的 <runtime> 节点中添加如下代码:

    <legacyUnhandledExceptionPolicy enabled="1"/>  
    

    加上了这个配置之后,Appdomain.CurrentDomain.UnhandledException 事件的 IsTerminating 就变成了 false 啦!也就是说,程序并不会因为这次的异常而崩溃退出。

    既然你通过这个配置节点于微软达成了契约,你就需要好好地在 Appdomain.CurrentDomain.UnhandledException 事件中写好异常的恢复逻辑。如果不好好恢复,小心有些致命的异常会导致你的程序出现雪崩式的错误,最终 Windows 还是会通过 CorruptedStateException 把你干掉的!

  • 相关阅读:
    lkl风控.随机森林模型测试代码spark1.6
    result源码
    categorys源码
    layer
    middle源码
    titlesplit源码
    201707舆情分析系统代码
    201707舆情分析系统阶段总结
    《理财市场情绪监测系统》代码实现【3】之情感极值表入库
    《理财市场情绪监测系统》代码实现【2】之爬虫数据解析
  • 原文地址:https://www.cnblogs.com/dongweian/p/15405702.html
Copyright © 2020-2023  润新知