AppDomain类型支持一些事件,允许向有关部分通报运行程序中的重要情况。表8.1列出了这些事件,它们中有4个与程序集的加载器和解析器相关,有3个与进程的终结条件有关。DomainUnload正好在AppDomain卸裁之前调用。ProcessExit则在进程中的CLR终结前调用,UnhandleExceptiond在最后时刻充当异常处理程序,用于解决线程没有处理自身异常的情形。
运行时将最终处理未处理的事件。如果导致异常的线程在堆栈中没有对应的异常处理程序,那么,CLR将调用它自己的全局异常处理程序(global exception handler)。该全局异常处理程序是应用程序恢复错误的最后机会,当然,依靠全局异常处理程序并不是很好的设计方式,与其让有问题的线程长时间地存在,不如将需要恢复的异常范围降低到最小。
CLR的全局异常处理程序首先检查配置,看是否需要附加调试器。这个配置可基于每进程、每用户以及每机器的原则进行设置。在Windows NT 下,CLR从处理程序范围的环境变量(COMPLUS_DbgJITDebugLaunchSetting)中,找出每处理程序的配置。对于每用户和每机器设置,CLR从注册键中读取值。如图8.4研示,DbgJITDebugLaunchSetting可以是O、l或者2。如果DbgJITDebUgLaunchsetting为l,那么,CLR就不会附加调试器,如果DbgJITDebuGLaunchSeting为2,则CLR附加JIT调试器。CLR从注册表中读取将要使用的调试器。默认情况下,DbgManageDebugger注册值指向vs7JIT.ExE;默认情况下,DbgManageDebugger注册值指向VS7JITEXE,它首先提示用户选择调试器。图8.5展示了VS7JITEXE的初始提示。如果DbgJITDebugLaunchsetting为O,那么,CLR将提醒用户,让其找出是否有附加的调试器。图8.6就是这个对话框。如果用户选择Cancel,接下来的过程就如同DbgJITDebugLaunch Setting为2的情形:如果用户选择OK,接下来的过程就如同DbgJITDebugLaunchSetting为l的情形。
在用户选择完附加调试器之后,CLR将检查是否有应用程序注册到UnhandledException事件处理程序。异常处理程序必须从默认域注册,而默认域是CLR创建的初始域。从子域中注册事件处理程序不会有任何效果。
示例8.4说明了UnhandedException事件处理程序的用法。CLR在终结进程之前,将运行我们的处理程序。假如没有注册UnhandledException事件处理程序,那么由异常引起的堆栈转储信息将打印到控制台上。图8.7为未处理异常处理的整个过程。
示例8.4 注册一个未处理异常的事件处理程序
using System;
class BadApp
{
//这个方法是我们的处理程序
static void AppHurts(Object sender,UnhandleExceptionEventArgs args){
Exception offender=args.ExceptionObject;
if(args.IsTerminating)
CleanUpbeforeDying();
}
static void Main()
{
//这将注册用于整个过程的处理程序
AppDomain.CurrentDomain.UnhandledException+=
new UnhandledExceptionEventHandler(Apphurts);
int x=3/0; //导致异常
}
}
有4个AppDomain事件与程序集解析和加载有关。当新的程序集被成功加载时,通过其中一个事件(AssemblyLoad)通知有关部分。当程序集解析器不能解析一个类型(TypeResolve)、程序集(AssemblyResolye)和清单资源( ResourccResolve)时,则使用这些事件的另外3个。对于这3个事件,CLR将为事件处理程序提供机会,用于产生System.Reflection Assembly对象以满足请求,这里需要注意的是:解析器之所以调用这3个方法,只是因为通过所有的标准技术,都无法找到所期望的程序集。这些事件主要对程序集解析器有用,它实现了特定应用程序的最后机会。程序集解析器使用某种应用程序相关的策略,用于将需要的AssemblyName转换为基本代码(codebase),基本代码可以被传递给Assembly.LoadForm。
示例8.5使用AssemblyResolve事件提供了用于查找程序集的备份策略,在本例中,CLR将所需程序集的单个名字加入到绝对路径winodows\system32中。在新的路径名被构建之后,事件处理程序使用Assembly LoadForm方法将控制权移交给底层的加载器。
示例8.5 在C#中的Retro-Programming in C#
using System;
using System.Reflection;
class oldhack{
static oldhack(){
//在类型初始化期,注册事件处理程序
AppDomain.CurrentDomain.AssemblyResolve+=
new ResolveEventHandler(Backstop);
}
//这是事件处理程序
static Assembly Backstop(object sender,ResolveEventArgs args){
//从显示名字中抽取简单名字
string displayName=args.Name;
string simpleName=displayName.Split(‘,’)[0];
//构建retre-file名字
string fullPath=String.Format(
“C:\\windows\\system32\\{0}.dll”,simpleName);
//委托给LoadFrom
return Assembly.LoadFrom(fullPath);
}
static void Main{
Console.WriteLine(MyCode.GetIt());
}
}