Winform异常处理之ThreadException、unhandledException及多线程异常处理

 

异常处理之ThreadException、unhandledException及多线程异常处理html

 

一:ThreadException和unhandledException的区别 windows

处理未捕获的异常是每一个应用程序起码有的功能,C#在AppDomain提供了UnhandledException 事件来接收未捕获到的异常的通知。常见的应用以下:   api

复制代码
代码
static void Main( string [] args)
{
AppDomain.CurrentDomain.UnhandledException
+= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e)
{
Exception error
= (Exception)e.ExceptionObject;
Console.WriteLine(
" MyHandler caught : " + error.Message);
}
复制代码

未捕获的异常,一般就是运行时期的BUG,因而咱们能够在UnhandledException 的注册事件方法CurrentDomain_UnhandledException中将未捕获异常的信息记录在日志中。值得注意的是,UnhandledException提供的机制并不能阻止应用程序终止,也就是说,CurrentDomain_UnhandledException方法执行后,应用程序就会被终止。多线程

上面咱们举的例子来自于控制台程序,UnhandledException能够在任何应用程序域中使用,在某些应用程序模型,如windows窗体程序,还存在ThreadException来处理 Windows 窗体线程中所发生的其未经处理的异常。即,在windows窗体程序中,使用 ThreadException 事件来处理 UI 线程异常,使用 UnhandledException 事件来处理非 UI 线程异常。ThreadException能够阻止应用程序终止。具体使用方法以下:  app

复制代码
代码
[STAThread]
static void Main()
{
Application.ThreadException
+= new ThreadExceptionEventHandler(UIThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException
+=
new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(
new ErrorHandlerForm());
}

private static void UIThreadException( object sender, ThreadExceptionEventArgs t)
{
try
{
string errorMsg = " Windows窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg
+ t.Exception.Message + Environment.NewLine + t.Exception.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的Windows窗体异常,应用程序将退出! " );
}
}

private static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex
= (Exception)e.ExceptionObject;
string errorMsg = " 非窗体线程异常 : \n\n " ;
MessageBox.Show(errorMsg
+ ex.Message + Environment.NewLine + ex.StackTrace);
}
catch
{
MessageBox.Show(
" 不可恢复的非Windows窗体线程异常,应用程序将退出! " );
}
}
复制代码

 除了Windows窗体程序,再来讲一下WPF程序。WPFUI线程和WindowsUI线程有点不同。WPFUI线程是交给一个叫作调度器的类:Dispatcher。代码以下:   dom

复制代码
代码
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);
}
catch
{
MessageBox.Show(
" 不可恢复的WPF窗体线程异常,应用程序将退出! " );
}
}

private void 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);
}
catch
{
MessageBox.Show(
" 不可恢复的WPF窗体线程异常,应用程序将退出! " );
}
}
复制代码

不管是Windows窗体程序仍是WPF程序,咱们都看到捕获的异常当中分为"窗体线程异常"和"非窗体线程异常"。如在Windows窗体程序中,若是在窗体线程中,       ide

throw new Exception( " 窗体线程异常 " );

将会触发ThreadException事件。  post

Thread t = new Thread((ThreadStart) delegate
{
throw new Exception( " 非窗体线程异常 " );
});
t.Start();

 将会触发UnhandledException事件,而后整个应用程序会被终止。this

 

二:多线程异常处理spa

 

多线程的异常处理,要采用特殊的作法。如下的处理方式会存在问题:   

复制代码
代码
try
{
Thread t
= new Thread((ThreadStart) delegate
{
throw new Exception( " 多线程异常 " );
});
t.Start();
}
catch (Exception error)
{
MessageBox.Show(error.Message
+ Environment.NewLine + error.StackTrace);
}
复制代码

 应用程序并不会在这里捕获线程t中的异常,而是会直接退出。从.NET2.0开始,任何线程上未处理的异常,都会致使应用程序的退出(先会触发AppDomain的UnhandledException)。上面代码中的try-catch实际上捕获的仍是当前线程的异常,而t是属于新起的异常,因此,正确的作法应该是:  

复制代码
代码
Thread t = new Thread((ThreadStart) delegate
{
try
{
throw new Exception( " 多线程异常 " );
}
catch (Exception error)
{
MessageBox.Show(
" 工做线程异常: " + error.Message + Environment.NewLine + error.StackTrace);
}
});
t.Start();
复制代码

 也就是说,新起的线程中异常的捕获,能够将线程内部代码所有try起来。原则上来讲,每一个线程本身的异常应该在本身的内部处理完毕,不过仍旧有一个办法,能够将线程内部的异常传递到主线程。

在Windows窗体程序中,能够使用窗体的BeginInvoke方法来将异常传递给主窗体线程:  

复制代码
代码
Thread t = new Thread((ThreadStart) delegate
{
try
{
throw new Exception( " 非窗体线程异常 " );
}
catch (Exception ex)
{
this .BeginInvoke((Action) delegate
{
throw ex;
});
}
});
t.Start();
复制代码

上文的代码将最终引起主线程的Application.ThreadException。最终的结果看起来有点像:  

 

WPF窗体程序中,你能够采用以下的方法将工做线程的异常传递到主线程:  

复制代码
代码
Thread t = new Thread((ThreadStart) delegate
{
try
{
throw new Exception( " 非窗体线程异常 " );
}
catch (Exception ex)
{
this .Dispatcher.Invoke((Action) delegate
{
throw ex;
}
);
}
});
t.Start();
复制代码

WPF窗体程序的处理方式与Windows窗体程序比较,有两个颇有意思的地方:

第一个是,在Windows窗体中,咱们采用的是BeginInvoke方法。你会发现使用Invoke方法,并不能引起主线程的Application.ThreadException。而在WPF窗体程序中,不管是调度器的Invoke仍是BeginInvoke方法都能将异常传递给主线程。

第二个地方就是InnerException。WPF的工做线程异常将会抛到主线程,变成主线程异常的InnerException,而Windows窗体程序的工做线程异常,将会被吃掉,直接变为null,只是在异常的Message信息中保存工做线程异常的Message。

 

三:ASP.NET异常处理

咱们都知道ASP.NET的全局异常处理方法是Global中的Application_Error方法。我曾经查过ASP.NET的Appdomain.CurrentDomain.unhandledException,结果用反射获得的结果,unhandledException所注册的事件方法根本不是这个方法。联想到ASP.NET页面,包括这个全局处理类,都是交给aspnet_isapi.dll处理的,而aspnet_isapi.dll不是一个托管程序集。因此,应该理解为,ASP.NET的未捕获异常的处理,不一样于托管异常(即CLR异常),而是交给aspnet_isapi.dll这个非托管DLL处理的。

 

 补充说明:

AppDomain.CurrentDomain.FirstChanceException事件会在First Chance时触发。保留部分First Chance有助于排查某些复杂的问题。我一般会保存最近十条First Chance异常,程序完全崩溃时输出到log。
AppDomain.CurrentDomain.UnhandledException事件会在未捕获的异常抛出时触发。这个时候你的程序基本上挂掉了,因此要输出到log。
对于WPF程序,Application.Current.DispatcherUnhandledException会在Dispatcher中未捕获的异常抛出时触发。一般这个时候你的程序已经要挂了,也要输出到log。

 

出处:https://www.cnblogs.com/luminji/archive/2011/01/05/1926033.html

相关文章
相关标签/搜索