当我们将第三方的DLL加载到进程中意味着冒险,DLL中的代码很容易破坏应用程序的数据结构和代码,DLL还可以尝试利用应用程序的安全上下文来访问它本来无权访问的资源,所以CLR就有了AppDomain功能来解决这些问题。
CLR COM服务器初始化时,会创建一个AppDomain。AppDomain是一组程序集的逻辑容器。CLR初始化时创建的第一个AppDomain称为默认AppDomain,这个默认的AppDomain只有在Windows进程终止时才会被销毁。
除了默认的AppDomain,正在使用非托管COM接口方法或者托管类型方法的一个宿主还可以指示CLR创建额外的AppDomain。AppDomain唯一的作用就是进行隔离,下面是AppDomain的具体功能:
1、一个AppDomain中的代码创建对象不能由另一个AppDomain中的代码直接访问。
2、AppDomain可以卸载
3、AppDomain可以单独保护
4、AppDomain可以单独实施配置
寄宿了CLR和两个AppDomain的一个Windows进程
上图演示了一个Windows进程,其中运行着一个CLR COM服务器。该CLR当前管理着2个AppDomain,每个AppDomain都有自己的Loader堆,每个Loader堆都记录了自AppDomain创建以来自己访问过哪些类型。
每个AppDomain都有自己的程序集,尽管有的程序集被加载到两个AppDomain中,如System.dll,如果两个AppDomain都使用来自System.dll的一个类型,那么在两个AppDomain的Loader堆中,都会为同一个类型分配一个类型对象;类型对象的内存不会由两个AppDomain共享。虽然这是中内存浪费,但是AppDomain的全部目的是提供隔离性,要求卸载某个AppDomain并释放它的所有资源的同时,不会对其它AppDomain产生负面影响。
有的程序集就要由多个AppDomain使用,典型的就是MSCorLib.dll,是一种”AppDomain中立”的方式加载,然而共享资源的代价就是程序集永远不能卸载,唯一的办法是终止该进程。
跨越AppDomain边界访问对象
using System;
using System.Threading;
using System.Reflection;
using System.Runtime.Remoting;
namespace AppDomains
{
class Program
{
static void Main(string[] args)
{
Marshalling();
Console.ReadKey();
}
private static void Marshalling()
{
//返回当前线程正在其中运行的Domain
AppDomain adCallingThreadDomain = Thread.GetDomain();
/*
* 每个AppDomain都被赋予了一个友好字符串名称(有利于调试)
*/
//获取此应用程序域的友好名称
String callingDomainName = adCallingThreadDomain.FriendlyName;
Console.WriteLine("Default AppDomain's friendly name={0}", callingDomainName);
//获取并显示我们的AppDomain中包含了"Main"方法的程序集
String exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine("Main assembly={0}", exeAssembly);
AppDomain ad2 = null;
/*DEMO 1:使用Marshal-by-Reference进行跨AppDomain通信*/
Console.WriteLine("{0}Demo #1", Environment.NewLine);
//新建一个AppDomain(安全性和配置匹配于当前AppDomain)
ad2 = AppDomain.CreateDomain("AD #2", null, null);
MarshalByRefType mbrt = null;
//将我们的程序集加载到新AppDomain中,构造一个对象,把它封送回我们的AppDomain(实际上得到对一个代理的引用)
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "AppDomains.MarshalByRefType");
Console.WriteLine("Type={0}",mbrt.GetType());//CLR在类型上撒谎了
//证明得到的是对一个代理对象的引用
Console.WriteLine("Is proxy={0}",RemotingServices.IsTransparentProxy(mbrt));
/*看起来像是在MarshalByRefType上调用的一个方法
* 实则不然,我们是在代理上类型上调用一个方法
* 代理使线程转至拥有对象的那个AppDomain,并在真实的对象上调用这个方法
*/
mbrt.SomeMethod();
//卸载新的AppDomain
AppDomain.Unload(ad2);
//mbrt引用一个有效的代理对象;代理对象引用一个无效的AppDomain
try
{
//在代理类型上调用一个方法。AppDomain无效,造成抛出一个异常
mbrt.SomeMethod();
Console.WriteLine("Successful call.");
}
catch (AppDomainUnloadedException )
{
Console.WriteLine("Failed call.");
}
//****DEMO2: 使用Marshal-by-Value进行跨AppDomain通信****
Console.WriteLine("{0}Demo #2", Environment.NewLine);
ad2 = AppDomain.CreateDomain("AD #2", null, null);
//
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "AppDomains.MarshalByRefType");
//对象的方法返回所返回的对象的一个副本
//对象按值(而非按引用)封送
MarshalByValType mbvt = mbrt.MethodWithReturn();
//证明我们得到的不是对一个代理对象的引用
Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbvt));
//看起来是在MarshaByValType上调用的一个方法,实际也是如此
Console.WriteLine("Return object created "+mbvt.ToString());
//卸载新的AppDomain
AppDomain.Unload(ad2);
try
{
Console.WriteLine("Return object created " + mbvt.ToString());
Console.WriteLine("Successful call.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed Call.");
}
//****DEMO 3: 使用不可封送的类型进行跨AppDomain通信*****
Console.WriteLine("{0}Demo #3", Environment.NewLine);
ad2 = AppDomain.CreateDomain("AD #2", null, null);
//
mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "AppDomains.MarshalByRefType");
NonMarshalableType nmt = mbrt.MethodArgAndReturn(callingDomainName);
//这里永远执行不到...
}
}
//该类的实例可跨越AppDomain的边界"按引用封送"
public sealed class MarshalByRefType : MarshalByRefObject
{
public MarshalByRefType()
{
Console.WriteLine("{0} ctor running in {1}",
this.GetType().ToString(),Thread.GetDomain().FriendlyName);
}
public void SomeMethod()
{
Console.WriteLine("Executing in"+Thread.GetDomain().FriendlyName);
}
public MarshalByValType MethodWithReturn()
{
Console.WriteLine("Executing in "+Thread.GetDomain().FriendlyName);
MarshalByValType t = new MarshalByValType();
return t;
}
public NonMarshalableType MethodArgAndReturn(string callingDomainName)
{
Console.WriteLine("Calling from {0} to {1}.",
callingDomainName,
Thread.GetDomain().FriendlyName
);
NonMarshalableType t = new NonMarshalableType();
return t;
}
}
//该类的实例可跨越AppDomain的边界“按值封送”
[Serializable]
public sealed class MarshalByValType : Object
{
public DateTime m_creationTime = DateTime.Now;
public MarshalByValType()
{
Console.WriteLine("{0} ctor running in {1} ,Created on {2:D}",
this.GetType().ToString(),
Thread.GetDomain().FriendlyName,
m_creationTime
);
}
public override string ToString()
{
return m_creationTime.ToLongDateString();
}
}
//该类的实例不能跨AppDomain边界进行封送
//[Serializable]
public sealed class NonMarshalableType : Object
{
public NonMarshalableType()
{
Console.WriteLine("Executing in "+Thread.GetDomain().FriendlyName);
}
}
}