关于未捕获异常的处理(WPF)

这一篇文章来谈谈对于WPF应用程序开发中的未捕获异常的处理。

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

image

备注:这个截图是在Windows 8上面做的,其他操作系统看到的界面可能略有不同。

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

image

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

image

然后是一个Application Error的事件

image

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

 

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

对于UI线程的未处理异常,我们可以通过监控下面这个事件来处理

Application.Current.DispatcherUnhandledException   http://msdn.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx

一个参考代码如下:

using System;
using System.Windows;

namespace WpfApplicationExceptionSample
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
        }


        void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);//这里通常需要给用户一些较为友好的提示,并且后续可能的操作

            e.Handled = true;//使用这一行代码告诉运行时,该异常被处理了,不再作为UnhandledException抛出了。
        }
    }
}

运行的效果大致如下

image

 

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

AppDomain.CurrentDomain.UnhandledException  http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx 

一个参考代码如下

using System;
using System.Windows;

namespace WpfApplicationExceptionSample
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show("我们很抱歉,当前应用程序遇到一些问题,该操作已经终止,请进行重试,如果问题继续存在,请联系管理员.", "意外的操作", MessageBoxButton.OK, MessageBoxImage.Information);
        }


    }
}

 

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

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

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="1"/>
  </runtime>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration>

 

这里的legacyUnhandledExceptionPolicy,如果enabled=1的话,用意是使用早期版本的异常处理策略。

1
0
(请您对文章做出评价)
« 上一篇: 牛刀小试:使用Reactive Extensions(Rx),对短时间内多次发生的事件限流
» 下一篇: 一个在ASP.NET中利用服务器控件GridView实现数据增删改查的例子

posted on 2013-08-25 16:39 陈希章 阅读(687) 评论(6编辑 收藏

评论

#1楼 2013-08-25 23:18 HenryZhu  

谢谢分享,这个是我一直在想的问题,今天看到方法很开心,但有个问题,早期版本的异常处理策略是怎样的?
  

#2楼[楼主2013-08-26 08:44 陈希章  

@HenryZhu
早期版本的策略就是说如果我们有处理那个事件,就不再往下传播这个异常了。其实我觉得这才是合理的。我也没有深入探究为什么新版本要改变这个行为。
  

#3楼 2013-08-26 12:01 zhangweiwen  

MSDN上面已经明确说明,AppDomain.CurrentDomain.UnhandledException只用于记录日志,清理资源等。如果想统一处理子线程的异常,可以使用统一的方式进入子线程,再把异常抛回UI线程。如果你的子线程的需要特别定制,那只能为它单独提供try/catch。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private  void  BgInvoke(Action action)
{
     Task.Factory.StartNew(() =>
     {
         try
         {
             action();
         }
         catch  (Exception ex)
         {
             Dispatcher.Invoke( new  Action(() =>
             {
                 throw  ex;
             }));
         }
     });
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值