寄宿和CLR
寄宿允许任何Windows应用程序使用公共语言运行库(CLR)的特征。它允许已有的应用程序至少局部使用托管代码编写。它为应用程序提供了通过编程支持自定义和可扩展性的能力。
在开发CLR时,微软实际上将CLR作为一个COM服务器实现在一个DLL内。微软为CLR定义了一个标准的COM接口,并且为该接口和COM服务器分配了GUID。安装.NET Framework时,表示CLR的COM服务器就像其他的COM服务器一样别注册到Windows的注册表里。详细信息可以看C++头文件MSCorEE.H。在这个头文件里定义了一些GUID和非托管的ICorRuntimeHost接口定义。
任何Windows应用程序都可以寄宿CLR。非托管应用程序宿主调用CorBindToRuntimeEx函数或者另一个相似的函数来创建CLR COM的实例。CorBindToRuntimeEx函数在MSCorEE.dll文件中实现。该DLL被称为“垫片”(shim)。他的职责是判断创建何种版本的CLR,但他本是不包含CLR COM服务器。
一台机器可以安装多个版本的CLR,但在机器中只有一个版本的MSCorEE.dll文件。安装在机器上的MSCorEE.dll的版本是那个随安装在机器上的最新版本的CLR一起发布的MSCorEE。dll的版本。CLR本身并不在MECorEE.dll文件中实现,它在一个MSCorWks.dll文件中实现。
Windows进程完全不必加载CLR,只有需要在进程中执行托管代码时,才需要加载CLR。一个Windows进程只可以加载一个版本的CLR。如果一个宿主进程调用了CorBindToRuntimeEx函数多次,那么每次都会返回同一个ICLRRuntimeHost指针。
一旦CLR加载到Windows的进程,就永远不会被卸载,要想卸载进程中的CLR,只能终止进程,导致Windows清理该进程使用的所有资源。
应用程序域(AppDomain)
应用程序域允许第三方没有经过信任的代码在已存在的进程中运行,并且能够保证应用程序的数据结构、代码以及安全上下文不被利用或者不会遭遇安全风险。
当CLR COM服务器开始初始化时,它就会创建一个应用程序域。一个应用程序域是一组程序集的一个容器。CLR初始化时创建的第一个应用程序域成为默认应用程序(default AppDomain),该应用程序域只有在进程终止时才会销毁。
宿主可以指示CLR创建额外的应用程序域。应用程序域的全部目的就是提供隔离性。有以下几个方面:
一个应用程序域的代码创建的对象不能被另一个应用程序域中的代码直接访问。
应用程序域可以被卸载。
应用程序域可以单独实施安全策略。
应用程序域可以单独实施配置策略。
上图为一个单独的Windows进程(来自CLR via C#),该进程中运行着一个CLR COM服务器。该CLR中有两个应用程序域(AppDomain)。每个应用程序域都有自己的加载器堆。如System.dll被两个AppDomain使用,那么这两个AppDomain中都会有该类型的一个对象,类型对象的内存不互相共享。 不对类型对象或者本地代码的内存进行共享看上去是不经济的。但是AppDomain的目的就是为了提供这种隔离性。另外,还有一些特殊的程序集如MSCorLib.dll,它需要被所有的应用程序域共享,那么他就以一种对应用程序域保持中立的方式被加载,由CLR维护一个特殊的加载器堆。这样的程序机永远不会被卸载,回收他们的唯一方式就是终止Windows进程。
卸载一个应用程序域可以导致CLR卸载应用程序域中的所有程序集,并且释放应用程序域的加载器堆。可以调用AppDomain的Unload方法实现。
跨应用程序域访问对象时有三种方式:按引用封送处理语义、按值封送处理语义、不进行封送处理。
线程与应用程序域的关系
在windows中,线程通常在进程的上下文中创建,而且线程的生存期与进程的生存期相同,但是线程与应用程序域没有一对一的关系。应用程序域时CLR的特征,Windows对应用程序域一无所知。因为多个应用程序域可以位于一个Windows进程中,所以一个线程可以执行一个应用程序域中的代码,然后又执行另一个应用程序域中的代码。从CLR的角度看,线程每次只能执行一个应用程序域中的代码。线程可以通过Thead的静态方法GetDomain来请求CLR当前正在那个应用程序域中执行。线程还可以查询AppDomain的CurrentDomain来获得同样的信息。