Value | Expected Domains in Process | Each Domain Expected to Run ... | Code for MSCORLIB | Code for Assemblies in GAC | Code for Assemblies not in GAC |
---|---|---|---|---|---|
SingleDomain | One | N/A | Per-process | Per-domain | Per-domain |
MultiDomain | Many | Same Program | Per-process | Per-process | Per-process |
MultiDomainHost | Many | Different Programs | Per-process | Per-process | Per-domain |
在C++中加載和卸載DLL是一件很容易的事,LoadLibrary和FreeLibrary讓你可以輕易的在程序中加載DLL,而後在任何地方 卸載。在C#中咱們也能使用Assembly.LoadFile實現動態加載DLL,可是當你試圖卸載時,你會很驚訝的發現Assembly沒有提供任何 卸載的方法。這是因爲託管代碼的自動垃圾回收機制會作這件事情,因此C#不提供釋放資源的函數,一切由垃圾回收來作。
這引起了一個問題,用Assembly加載的DLL可能只在程序結束的時候纔會被釋放,這也意味着在程序運行期間沒法更新被加載的DLL。而這個功能在某 些程序設計時是很是必要的,考慮你正在用反射機制寫一個查看DLL中全部函數詳細信息的程序,程序提供一個菜單讓用戶能夠選擇DLL文件,這時就須要讓程 序可以卸載DLL,不然一旦用戶從新獲得新版本DLL時,必需要從新啓動程序,從新選擇加載DLL文件,這樣的設計是用戶沒法忍受的。
C#也提供了實現動態卸載DLL的方法,經過AppDomain來實現。AppDomain是一個獨立執行應用程序的環境,當AppDomain被卸載的 時候,在該環境中的全部資源也將被回收。關於AppDomain的詳細資料參考MSDN。下面是使用AppDomain實現動態卸載DLL的代碼,
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Reflection;
namespace UnloadDll
{
class Program
{
static void Main(string[] args)
{
string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
AppDomain ad = AppDomain.CreateDomain("DLL Unload test");
ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");
obj.LoadAssembly();
obj.Invoke("TestDll.Class1", "Test", "It's a test");
AppDomain.Unload(ad);
obj = null;
Console.ReadLine();
}
}
class ProxyObject : MarshalByRefObject
{
Assembly assembly = null;
public void LoadAssembly()
{
assembly = Assembly.LoadFile(@"TestDLL.dll");
}
public bool Invoke(string fullClassName, string methodName, params Object[] args)
{
if(assembly == null)
return false;
Type tp = assembly.GetType(fullClassName);
if (tp == null)
return false;
MethodInfo method = tp.GetMethod(methodName);
if (method == null)
return false;
Object obj = Activator.CreateInstance(tp);
method.Invoke(obj, args);
return true;
}
}
}
注意:
1. 要想讓一個對象可以穿過AppDomain邊界,必需要繼承MarshalByRefObject類,不然沒法被其餘AppDomain使用。
2. 每一個線程都有一個默認的AppDomain,能夠經過Thread.GetDomain()來獲得shell