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线程错误处理函数,让它把这个错误截获,从而不会造成程序崩溃。