CSharp UnhandledException

在我们的程序中,不可避免的会出现异常,那么就需要就异常进行处理,否则就会出现bug。

下面将提到我经常使用的两种桌面程序对UnhandledException的处理。

1、首先,在WinForm程序中,需要在program.cs中添加以下代码。

static class Program
{
   /// <summary>
   /// 应用程序的主入口点。
   /// </summary>
  [STAThread]
  static void Main()
  {
     try
     {
         //处理未捕获的异常   
          Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
         //处理UI线程异常   
          Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
         //处理非UI线程异常   
          AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
                
         Application.EnableVisualStyles();
         Application.SetCompatibleTextRenderingDefault(false);
         Application.Run(new frmlogin());
     }
     catch(Exception ex)
     {
         frmBug f = new frmBug(ex.Message);//友好提示界面
          f.ShowDialog();
     }

  }
        
  static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)   
  {
      string str = "";
      Exception error = e.Exception as Exception;   
      if (error != null)   
      {   
          str =string.Format("出现应用程序未处理的异常\n异常类型:{0}\n异常消息:{1}\n异常位置:{2}\n",   
               error.GetType().Name, error.Message, error.StackTrace);    
      }   
      else  
      {   
          str =string.Format("应用程序线程错误:{0}", e);   
      }
      frmBug f = new frmBug(str);//友好提示界面
       f.ShowDialog();
   }   
  
   static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)   
   {
      string str = "";
      Exception error = e.ExceptionObject as Exception;   
      if (error != null)   
      {   
         str =string.Format("Application UnhandledException:{0};\n堆栈信息:{1}", error.Message, error.StackTrace);   
      }   
      else  
      {   
         str =string.Format("Application UnhandledError:{0}", e);   
      }
      frmBug f = new frmBug(str);//友好提示界面
       f.ShowDialog();
   }  
}

 

2、在WPF中,需要在app.xaml.cs中添加以下代码

public App()
{
  this.DispatcherUnhandledException +=new DispatcherUnhandledExceptionEventHandler(Application_DispatcherUnhandledException);
  AppDomain.CurrentDomain.UnhandledException +=new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
  try
  {
    Exception ex = e.ExceptionObject as Exception;
    string errorMsg ="非WPF窗体线程异常 : \n\n";
    MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);

// Update : keep application from shutdown
    
e.Dispatcher.Invoke(new Action(() => { throw ex; }));
  }
  catch
  {
    MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!");
  }
}

privatevoid Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
  try
   {
    Exception ex = e.Exception;
    string errorMsg ="WPF窗体线程异常 : \n\n";
    MessageBox.Show(errorMsg + ex.Message + Environment.NewLine + ex.StackTrace);
    e.Handled=true;
  }   catch   {     MessageBox.Show("不可恢复的WPF窗体线程异常,应用程序将退出!");   } }

 总结:经过我自己在WinForm下测试,在主UI界面引发的异常,能够被ThreadException捕获,并处理。

    但是对于非主UI线程内的异常,即子线程内的异常,虽然会被UnhandledException处理,但是仍然会引起程序挂掉。

    所以,我觉得这里应该使用CurrentDomain.UnhandledException处理非UI线程异常,不太好,最好还是自己写一个Invoke函数,

    每次当非UI线程内出现异常时,将其抛至主UI线程中进行捕获,目前对于为什么程序仍然会挂掉,还没有研究出来。

    但是,同样的原理,虽然没有在WPF中测试一下,我觉得应该也是一样的,但是之前在写WPF程序时,发现有一种异常,

    是主UI线程也无法捕获的,现在也没有研究出什么眉目,那就是动态引用DLL,但是DLL不存在时,程序出现异常,但是不能采用这种方法捕获异常,

    目前也没有发现原因,也不知道如何去解决,可能这个异常是由FM抛出来的吧,应用引用DLL应该不是由程序自己来执行的,所以这种异常没有被捕获。

    但是,这个只是一个猜测,具体的还需要研究。

研究结果:之前一直对一个问题很有疑惑,那么就是明明我写了上面的很大一段代码,异常也被捕获、显示输出了。但是,程序仍然挂掉了。

     今天突然发现,在WPF的DispatcherUnhandledExceptionEventArgs中有一个Handled属性,类似路由事件中相同的一个属性,

     通过设置这个属性为true,可以防止异常继续传递,所以防止程序挂掉。那么之前程序挂掉的原因也很明显了,就是因为异常继续传递了。

     这个只是在处理主UI线程时,才有这样的特性,当处理非UI线程异常时,程序还是会挂掉,所以唯一解决的办法就是在工作线程中,

     通过Dispatcher来将异常抛到主线程中,让我们这里写的DispatcherUnhandledExceptionEventArgs来处理这个异常。

更新:之前的非UI线程处理后程序仍然退出的原因,貌似找到了。通过在处理代码后面加上 e.Dispatcher.Invoke(new Action(() => { throw ex; }));。

     异常会先被非UI线程处理函数处理,然后被UI线程处理,这句话的意思就是把异常再抛给UI线程错误处理函数。

     所以,之前仍然出错的原因是:虽然异常被我们捕获;但是,异常仍然被再次抛出,我们写的处理函数只是接收到了这个消息,

     但是并没有截获它,所以我们将其再转发给UI线程错误处理函数,让它把这个错误截获,从而不会造成程序崩溃。

 

posted on 2012-12-30 22:03  littlelemon  阅读(814)  评论(0编辑  收藏  举报