一、了解应用程序域
应用程序域 (application domain) (AppDomain)一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点开始,沿着对象激活的序列的任何位置)。应用程序域有助于将在一个应用程序中创建的对象与在其他应用程序中创建的对象隔离,以使运行时行为可以预知。在一个单独的进程中可以存在多个应用程序域。
以前使用进程边界来隔离在同一台计算机上运行的应用程序。每一个应用程序被加载到单独的进程中,这样就将该应用程序与在同一台计算机上运行的其他应用程序相隔离。
隔离这些应用程序的原因在于内存地址是与进程相关的;在目标进程中,不能通过任何有意义的方式使用从一个进程传递到另一个进程的内存指针。此外,您不能在两个进程间进行直接调用。您必须代之以使用代理,它提供一定程度的间接性。
托管代码必须先通过一个验证过程,然后才能运行(除非管理员已授权跳过该验证)。此验证过程将验证以下内容:这些代码是否会尝试访问无效的内存地址?是否会尝试执行某些导致进程(该代码运行时所在的进程)无法正常进行的其他操作?通过此验证测试的代码将被认为是类型安全的。由于公共语言运行库能够验证代码是否为类型安全的代码,所以它可以提供与进程边界一样大的隔离级别,而其性能开销则要低得多。
应用程序域提供安全而通用的处理单元,公共语言运行库可使用它来提供应用程序之间的隔离。您可以在具有同等隔离级别(存在于单独的进程中)的单个进程中运行几个应用程序域,而不会造成进程间调用或进程间切换等方面的额外开销。在一个进程内运行多个应用程序的能力显著增强了服务器的可伸缩性。
隔离应用程序对于应用程序安全也是十分重要的。例如,您可以在单个浏览器进程中运行几个 Web 应用程序中的控件,同时使这些控件不能访问彼此的数据和资源。
应用程序域所提供的隔离具有以下优点:
在一个应用程序中出现的错误不会影响其他应用程序。因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。
能够在不停止整个进程的情况下停止单个应用程序。使用应用程序域使您可以卸载在单个应用程序中运行的代码。
注意 不能卸载单个程序集或类型。只能卸载整个域。
在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。为了强制实施此隔离,公共语言运行库禁止在不同应用程序域中的对象之间进行直接调用。要在各域之间传递对象,可以复制这些对象,或通过代理访问这些对象。如果复制对象,那么对该对象的调用为本地调用。也就是说,调用方和被引用的对象位于同一应用程序域中。如果通过代理访问对象,那么对该对象的调用为远程调用。在此情况下,调用方和被引用的对象位于不同的应用程序域中。域间调用所采用的远程调用结构与两个进程间的调用或两台计算机间的调用结构相同。
代码行为的作用范围由它运行所在的应用程序决定。换言之,应用程序域将提供应用程序版本策略等配置设置、它所访问的任意远程程序集的位置,以及加载到该域中的程序集的位置信息。
二、程序域编程
应用程序域为公共语言运行库提供隔离单元。它们在进程中创建和运行。应用程序域通常由运行库宿主创建,运行库宿主是负责将运行库载入进程并在应用程序域中执行用户代码的应用程序。
运行库宿主创建进程和默认应用程序域,并在其中运行托管代码。
运行库宿主包括 ASP.NET、Microsoft Internet Explorer 和 Windows 外壳程序。
对于多数应用程序,您不需要创建自己的应用程序域,运行库宿主将为您创建所有必要的应用程序域。
但是,如果您的应用程序需要隔离代码或使用并卸载 DLL,您可以创建并配置其他应用程序域。
1、创建应用程序域
当需要应用程序域时,公共语言运行库宿主会自动创建它们。不过,您可以创建自己的应用程序域并将它们加载到需要亲自管理的程序集中。您也可以创建从中执行代码的应用程序域。
若要创建新的应用程序域,可使用 System.AppDomain 类中某个重载的 CreateDomain 方法。您可以为应用程序域命名并按该名称来引用应用程序域。
2、卸载应用程序域
当您完成使用应用程序域时,可使用 System.AppDomain.Unload 方法将其卸载。Unload 方法会正常关闭指定的应用程序域。卸载过程中,没有新线程可以访问该应用程序域,并且会释放该应用程序域特定的所有数据结构。
加载到应用程序域中的所有程序集都会被移除,无法再使用。如果应用程序域中的程序集不是特定于域的,则程序集的数据会保留在内存中,直到整个进程关闭。除了关闭整个进程,没有机制可以卸载非特定于域的程序集。在某些情况下,卸载应用程序域的请求不起作用,并导致 CannotUnloadAppDomainException。
3、配置应用程序域
您可以使用 AppDomainSetup 类为新应用程序域提供带有配置信息的公共语言运行库。创建自己的应用程序域时,最重要的属性是 ApplicationBase。其他 AppDomainSetup 属性主要由运行时宿主用于配置特殊的应用程序域。
ApplicationBase 属性定义应用程序的根目录,当运行时需要满足类型请求时,它在 ApplicationBase 属性指定的目录中探测包含该类型的程序集。
4、从应用程序域中检索安装程序信息
应用程序域的每个实例同时包含属性和 AppDomainSetup 信息。您可以使用 System.AppDomain 类从应用程序域中检索安装信息。此类提供了几个检索有关应用程序域的配置信息的成员。
您也可以查询应用程序域的 AppDomainSetup 对象,获取创建该对象时传递至域的安装信息。
5、影像复制程序集
用于应用程序域中的程序集通过影像复制可以在不卸载应用程序域的情况下进行更新。这对必须不间断使用的应用程序(如 ASP.NET 网站)尤为有用。
加载程序集时,公共语言运行库会将程序集文件锁定,因此只有卸载了程序集之后才能更新该文件。从应用程序域卸载程序集的唯一方法是卸载该应用程序域,因此在正常情况下,只有卸载了使用程序集的所有应用程序域之后才能在磁盘上更新程序集。
将应用程序域配置为影像复制文件时,来自应用程序路径的程序集被复制到另一个位置并从该位置进行加载。该副本被锁定,但原始程序集文件将取消锁定并可以进行更新。
三、示例代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace appDomain { class Program { static void Main(string[] args) { Program p = new Program(); //p.CreateAppDomain(); // p.UnloadAppDomain(); // p.SetUpAppDomain(); p.GetSetUpInfor(); Console.ReadLine(); } public void CreateAppDomain() { Console.WriteLine("Creating new AppDomain."); AppDomain domain = AppDomain.CreateDomain("MyDomain"); Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName); Console.WriteLine("child domain: " + domain.FriendlyName); } public void UnloadAppDomain() { Console.WriteLine("Creating new AppDomain."); AppDomain domain = AppDomain.CreateDomain("MyDomain", null); Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName); Console.WriteLine("child domain: " + domain.FriendlyName); AppDomain.Unload(domain); try { Console.WriteLine(); Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName); // The following statement creates an exception because the domain no longer exists. Console.WriteLine("child domain: " + domain.FriendlyName); } catch (AppDomainUnloadedException e) { Console.WriteLine("The appdomain MyDomain does not exist."); } } public void SetUpAppDomain() { AppDomainSetup domaininfo = new AppDomainSetup(); domaininfo.ApplicationBase = "f://work//development//latest"; // Create the application domain. AppDomain domain = AppDomain.CreateDomain("MyDomain", null, domaininfo); // Write application domain information to the console. Console.WriteLine("Host domain: " + AppDomain.CurrentDomain.FriendlyName); Console.WriteLine("child domain: " + domain.FriendlyName); Console.WriteLine("Application base is: " + domain.SetupInformation.ApplicationBase); // Unload the application domain. AppDomain.Unload(domain); } public void GetSetUpInfor() { AppDomainSetup appDomainSetup = AppDomain.CurrentDomain.SetupInformation; Console.WriteLine("ConfigurationFile:" + appDomainSetup.ConfigurationFile); Console.WriteLine("ApplicationName:" + appDomainSetup.ApplicationName); Console.WriteLine("ApplicationBase:" + appDomainSetup.ApplicationBase); Console.WriteLine("CachePath:" + appDomainSetup.CachePath); Console.WriteLine("PrivateBinPath:" + appDomainSetup.PrivateBinPath); Console.WriteLine("ShadowCopyDirectories:" + appDomainSetup.ShadowCopyDirectories); } public void LoadAssembly() { // AppDomain.CurrentDomain.CreateInstanceFrom(); } } }
四、AppDomain 类信息
部分公共属性
名称 说明
ActivationContext 获取当前应用程序域的激活上下文。
ApplicationIdentity 获得应用程序域中的应用程序标识。
ApplicationTrust 获取说明授予应用程序的权限以及应用程序是否拥有允许其运行的信任级别的信息。
BaseDirectory 获取基目录,它由程序集冲突解决程序用来探测程序集。
CurrentDomain 获取当前 Thread 的当前应用程序域。
DomainManager 获得初始化应用程序域时主机提供的域管理器。
DynamicDirectory 获取目录,它由程序集冲突解决程序用来探测动态创建的程序集。
Evidence 获取与此应用程序域相关联的 Evidence,它用作安全策略的输入。
FriendlyName 获取此应用程序域的友好名称。
Id 获得一个整数,该整数唯一标识进程中的应用程序域。
RelativeSearchPath 获取相对于基目录的路径,在此程序集冲突解决程序应探测专用程序集。
SetupInformation 获取此实例的应用程序域配置信息。
ShadowCopyFiles 获取一个指示值,它表明加载到应用程序域中的所有程序集是否为影像复制的。
部分公共方法
名称 说明
AppendPrivatePath 将目录的指定名称追加到专用路径。
CreateDomain 创建新的应用程序域。
CreateInstance 创建在指定程序集中定义的指定类型的新实例
CreateInstanceFrom 创建在指定程序集文件中定义的指定类型的新实例。
CreateObjRef 创建一个对象,该对象包含生成用于与远程对象进行通信的代理所需的全部相关信息。
ExecuteAssembly 执行指定文件中包含的程序集。
Unload 卸载指定的应用程序域。
公共事件
名称 说明
AssemblyLoad 在加载程序集时发生。
AssemblyResolve 在对程序集的解析失败时发生。
DomainUnload 在即将卸载 AppDomain 时发生。
ProcessExit 当默认应用程序域的父进程存在时发生。
ReflectionOnlyAssemblyResolve 当程序集的解析在只反射上下文中失败时发生。
ResourceResolve 当资源解析因资源不是程序集中的有效链接资源或嵌入资源而失败时发生。
TypeResolve 在对类型的解析失败时发生。
UnhandledException 当某个异常未被捕获时出现。
五、参考信息:
http://msdn.microsoft.com/zh-cn/library/yb506139.aspxhttp://msdn.microsoft.com/zh-cn/library/system.appdomain_members(VS.80).aspx