异常捕获
异常捕获筛选器会判断抛出的异常类型是否和筛选器指定的类型相匹配,如果匹配成功,那么它会返回一个特殊的值通知CLR。CLR首先执行更低层次的堆栈中所有必要的finally块来清理其中启动的操作,展开调用堆栈。然后CLR才执行与抛出异常类型相匹配的catch块中的代码。
未处理异常
登记AppDomain.CurrentDomain.UnhandledException事件可以处理当前应用程序域中的由托管代码抛出的未处理异常(不能处理非托管代码抛出的未处理异常)。该事件的委托原型为:
public delegate void UnHandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
UnhandledExceptionEventArgs的IsTerminating属性告诉我们CLR是否会因为该未处理异常而中断应用程序。通常情况下,对于手工线程(使用System.Threading.Thread对象显式创建的线程)、线程池线程、终止化器线程(在垃圾收集器判定对象为不可达时,托管堆有一个线程专门执行对象的Finalize方法),CLR会忽略任何未处理异常,然后或中断线程、或将线程返回到线程中、或转而调用下一个对象的Finalize方法。如果是上面这三种线程出现了未处理异常,那么IsTerminating属性将返回false。如果是应用程序主线程或一个非托管线程中出现了未处理异常,那么IsTerminating属性将返回true。
Windows窗体中的未处理异常
如果一个继承自System.Exception的未处理异常出现在窗体消息处理过程中,那么catch块将会调用该窗体类型的虚方法OnThreadException,并将异常对象传递给它。System.Windows.Forms.Controls类型实现的OnThreadException又会调用Application的OnThreadException方法。默认情况下,该方法将显示一个异常提示对话框。
我们可以通过定义一个和System.Threading.ThreadExceptionEventHandler委托相匹配的方法,并将其登记到Application类型的静态事件ThreadException上来覆盖这个内建对话框。
Windows窗体只处理与CLS兼容的异常,与CLS不兼容的异常会跳出线程的消息循环,并沿着调用堆栈向更高一层传递。因此,如果我们希望同时显示或记录与CLS兼容和不兼容的异常,我们必须定义两个回调方法,一个登记在Application类型的ThreadException事件上,另一个登记在AppDomain类型的UnhandledException事件上。
ASP.Net Web窗体的未处理异常
ASP.Net在自己的try块中执行所有的Web窗体代码。如果我们的代码抛出了一个未处理异常,ASP.Net会捕获到该异常,并决定如何处理。
我们可以定义一个回调方法使其在某个特定的Web页面中出现未处理异常时能被调用。该回调方法由System.Web.UI.TemplateControl类提供的Error事件来登记。该类是System.Web.UI.Page和System.Web.UI.UserControl的基类。
我们还可以登记一个回调方法到System.Web.HTTPApplication类的Error事件来接受一个Web窗体应用程序内任何页面中出现的未处理异常通知。这样的代码一般加到Global.asax文件。
异常堆栈踪迹
System.Exception类型提供了一个公有只读属性StackTrace。在异常筛选器或catch块内,我们可以通过读取该属性来获取异常的堆栈踪迹。异常堆栈踪迹指出了异常经过的路径中所发生的事件和异常被抛出开始到异常被捕获为止之间所有的方法。
当一个异常被抛出时,CLR会在内部记录throw指令出现的位置。当有捕获筛选器接受该异常时,CLR又会记录异常被捕获的位置。
当我们抛出一个异常时,CLR会重新设置异常起始点。也就是说,CLR只记录最近一次异常抛出的位置。
void SomeMethod()
{
try {......}
catch (Exception e)
{
......
throw e; // CLR认为这里就是异常的起始点
}
}
// 如果我们重新抛出一个异常对象,CLR将不会设置其堆栈的起始点。
void SomeMethod()
{
try {......}
catch (Exception e)
{
......
throw ; // CLR不会重新设置异常的起始点
}
}
远程堆栈踪迹
如果客户端向服务器发出了一个请求,但是服务器代码却抛出了一个异常,那么该异常对象可以被封送处理传回客户端,且在客户端线程中重新抛出。同时,回传的异常中也包含了堆栈踪迹。