AppDomain and related
以下资料整理自MSDN
公共语言运行库已经过专门设计,支持各种类型的应用程序,包括从 Web 服务器应用程序到具有传统的丰富 Windows 用户界面的应用程序在内的所有应用程序。每种应用程序都需要一个运行库宿主来启动它。运行库宿主将该运行库加载到进程中,在该进程内创建应用程序域,并且将用户代码加载到该应用程序域中。
.NET Framework 附带有多种不同的运行库宿主,包括下表中列出的宿主。
运行库宿主 |
说明 |
ASP.NET |
将运行库加载到要处理 Web 请求的进程中。ASP.NET 还为将在 Web 服务器上运行的每个 Web 应用程序创建一个应用程序域。 |
Microsoft Internet Explorer |
创建要在其中运行托管控件的应用程序域。.NET Framework 支持下载和执行基于浏览器的控件。运行库通过 MIME 筛选器与 Microsoft Internet Explorer 的扩展性机制相连接,以创建要在其中运行托管控件的应用程序域。默认情况下,将为每个网站创建一个应用程序域。 |
外壳程序可执行文件 |
每次从外壳程序启动可执行文件时,都要调用运行库宿主代码来将控制权转给该运行库。 |
Microsoft 提供了一组 API,可供您编写自己的运行库宿主。
Unix环境下在操作系统之上提供的一套命令解释程序叫做外壳程序(shell).
外壳程序是操作员与操作系统交互的界面,操作系统再负责完成与机器硬件的交互。
所以操作系统可成为机器硬件的外壳,shell命令解析程序可称为操作系统的外壳。
应用程序域为隔离正在运行的应用程序提供了一种灵活而安全的方法。
因为应用程序域可以加载程序集,单个进程可以运行多个应用程序域,并具有在单独进程中所存在的隔离级别。在单个进程中运行多个应用程序提高了服务器伸缩性。
例如一个aspnet_wp.exe可以加载多个网站程序集。
下面的代码示例创建一个新的应用程序域,然后加载并执行以前生成的程序集 HelloWorld.exe,该程序集存储在驱动器 C 上。
static void Main()
{
// Create an Application Domain:
System.AppDomain newDomain = System.AppDomain.CreateDomain("NewApplicationDomain");
// Load and execute an assembly:
newDomain.ExecuteAssembly(@"c:\HelloWorld.exe");
// Unload the application domain:
System.AppDomain.Unload(newDomain);
}
用程序域具有以下特点:
- 必须先将程序集加载到应用程序域中,然后才能执行该程序集。
- 一个应用程序域中的错误不会影响在另一个应用程序域中运行的其他代码。
- 能够在不停止整个进程的情况下停止单个应用程序并卸载代码。不能卸载单独的程序集或类型,只能卸载整个应用程序域。
以前使用进程边界来隔离在同一台计算机上运行的应用程序。每一个应用程序被加载到单独的进程中,这样就将该应用程序与在同一台计算机上运行的其他应用程序相隔离。
隔离这些应用程序的原因在于内存地址是与进程相关的;在目标进程中,不能通过任何有意义的方式使用从一个进程传递到另一个进程的内存指针。
托管代码必须先通过一个验证过程,然后才能运行(除非管理员已授权跳过该验证)。此验证过程将验证以下内容:这些代码是否会尝试访问无效的内存地址?是否会尝试执行某些导致进程(该代码运行时所在的进程)无法正常进行的其他操作? 通过此验证测试的代码将被认为是类型安全的。由于公共语言运行库能够验证代码是否为类型安全的代码,所以它可以提供与进程边界一样大的隔离级别,而其性能开销则要低得多。
要在各域之间传递对象,可以复制这些对象,或通过代理访问这些对象。如果复制对象,那么对该对象的调用为本地调用。也就是说,调用方和被引用的对象位于同一应用程序域中。如果通过代理访问对象,那么对该对象的调用为远程调用。在此情况下,调用方和被引用的对象位于不同的应用程序域中。
应用程序域通常由运行时宿主以编程的方式来创建和操作。但是,有时应用程序还可能要和应用程序域结合起来使用。例如,应用程序可能将应用程序组件加载到域中以便能够在不停止整个应用程序的情况下卸载域(以及该组件)。
AppDomain 类是应用程序域的编程接口。此类包括各种方法,这些方法可以创建和卸载域、创建域中各类型的实例以及注册各种通知(如应用程序域卸载)。
AppDomain 方法 |
说明 |
创建新的应用程序域。建议使用此方法指定 AppDomainSetup 对象的重载形式。这是设置新域的各个属性的首选方式,这些属性包括应用程序基(即该应用程序的根目录)、域的配置文件的位置、以及公共语言运行时用于将程序集加载到域中的搜索路径等。 |
|
执行应用程序域中的程序集。这是一个实例方法,因此它可用来执行另一个应用程序域(您拥有对该域的引用)中的代码。 |
|
在应用程序域中创建指定类型的实例,并返回一个代理。使用此方法以避免将包含创建的类型的程序集加载到调用程序集中。 |
|
执行域的正常关闭。只有应用程序域中正在运行的所有线程都已停止或域中不再有运行的线程之后,才卸载该应用程序域。 |
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
public sealed class AppDomain : MarshalByRefObject,
_AppDomain, IEvidenceFactory
下面的代码将名为“example.exe”或“example.dll”的程序集加载到当前应用程序域中,从该程序集获取名为 Example 的类型,为该类型获取名为 MethodA 的无参数方法,然后执行该方法。
using System;
using System.Reflection;
public class Asmload0
{
public static void Main ()
{
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("example");
// Get the type to use.
Type myType = a.GetType("Example");
// Get the method to call.
MethodInfo mymethod = myType.GetMethod("MethodA");
// Create an instance.
Object obj = Activator.CreateInstance(myType);
// Execute the method.
mymethod.Invoke(obj,null);
}
}
此示例显示如何创建新的 AppDomain,在该新建 AppDomain 中实例化类型,以及与该类型的对象通信。此外,此示例还显示如何卸载导致对象被垃圾回收的 AppDomain。
using System;
using System.Reflection;
using System.Threading;
namespace appdomain
{
class Module1
{
public static void Main()
{
// Get and display the friendly name of the default AppDomain.
string callingDomainName = Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
// Get and display the full name of the EXE assembly.
string exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(exeAssembly);
// Construct and initialize settings for a second AppDomain.
AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationBase =
System.Environment.CurrentDirectory;
ads.DisallowBindingRedirects = false;
ads.DisallowCodeDownload = true;
ads.ConfigurationFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// Create the second AppDomain.
AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads);
// Create an instance of MarshalbyRefType in the second AppDomain.
// A proxy to the object is returned.
MarshalByRefType mbrt =
(MarshalByRefType) ad2.CreateInstanceAndUnwrap(
exeAssembly,
typeof(MarshalByRefType).FullName
);
// Call a method on the object via the proxy, passing the
// default AppDomain's friendly name in as a parameter.
mbrt.SomeMethod(callingDomainName);
// Unload the second AppDomain. This deletes its object and
// invalidates the proxy object.
AppDomain.Unload(ad2);
try
{
// Call the method again. Note that this time it fails
// because the second AppDomain was unloaded.
mbrt.SomeMethod(callingDomainName);
Console.WriteLine("Sucessful call.");
}
catch(AppDomainUnloadedException)
{
Console.WriteLine("Failed call; this is expected.");
}
Console.ReadKey(true);
}
}
// Because this class is derived from MarshalByRefObject, a proxy
// to a MarshalByRefType object can be returned across an AppDomain
// boundary.
public class MarshalByRefType : MarshalByRefObject
{
// Call this method via a proxy.
public void SomeMethod(string callingDomainName)
{
// Get this AppDomain's settings and display some of them.
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("AppName={0}, AppBase={1}, ConfigFile={2}",
ads.ApplicationName,
ads.ApplicationBase,
ads.ConfigurationFile
);
// Display the name of the calling AppDomain and the name
// of the second domain.
// NOTE: The application's thread has transitioned between
// AppDomains.
Console.WriteLine("Calling from '{0}' to '{1}'.",
callingDomainName,
Thread.GetDomain().FriendlyName
);
}
}
}