Assembly.Load 詳解(c#)

 咱們在使用C# 語言的Assembly.Load 來加載託管程序集並使用反射功能時,通常須要先經過Assembly.Load(), Assembly.LoadFrom() 等方法將目標託管程序集加載到當前應用程序域中,而後生成對應實例,最後再進行調用實例的屬性或者方法。html

通常狀況下,咱們調用Assembly.Load 一類方法是不會出問題的,可是對於如下幾種狀況Assembly.Load 方法沒法處理:緩存

  1. 程序集多是延遲簽名的。
  2. 程序集可能被CAS 策略保護。
  3. 宿主程序與目標程序集的處理器架構不一樣。
  4. 當加載目標程序集時,目標程序集中的方法可能正在運行。 (好比,模塊初始化)
  5. 程序集可能應用了綁定策略, 你可能不會獲得你想要的那個程序集。

咱們如今關注第四種狀況,由於這種狀況是最多見的。咱們思考如下幾個問題:架構

 

1. 爲何目標程序集的方法在運行時不容許再加載一次?app

準確地說是爲何在一個應用程序域(AppDomain)中加載後的程序集默認不容許再另一個應用程序域中加載?這是由於在第一次加載應用程序集時,Assemlby.Load 方法會將此程序集鎖住,以防止在本身使用過程當中應用程序集被其餘應用程序修改(通常指刪除)。這其實與Win32 API 中的CreateFile 函數行爲相似,咱們都知道,在 Windows 中去佔用一個文件最直接、最簡單的方式就是調用 CreateFile API 函數來打開文件。具體請參照:框架

http://blog.csdn.net/xt_xiaotian/article/details/6362450 dom

 

2. Assembly.Load 方法可否實如今加載目標程序集時不鎖定它?函數

咱們可使用以下代碼加載咱們的程序集:優化

byte[] buffer = System.IO.File.ReadAllBytes(yourFullfileNamePath);

//Load assembly using byte array

Assembly assembly = Assembly.Load(buffer);

後臺的實現是暫時先將目標程序集鎖定,而後把程序集內容複製到內存中,讀取後將程序集解鎖並從內存中加載目標程序集的拷貝。spa

若是你須要經過上面的方法加載GAC 中的程序集,那麼能夠經過以下代碼實現:.net

複製代碼
string assemblyName = "AssemblyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=fffb45e56dd478e3";

Assembly ass = Assembly.ReflectionOnlyLoad(assemblyName);

byte[] buffer = System.IO.File.ReadAllBytes(ass.Location);

Assembly assembly = Assembly.Load(buffer
複製代碼

 

3. Assembly.Load 方法的緩存

在咱們使用Assembly.Load 系列方法加載目標程序集時,可能有各類狀況致使加載失敗,最多見的是目標程序集不存在而致使加載失敗問題。失敗後咱們可能想要再加載一次或者加載屢次直到成功爲止,可是在.NET Framework 2.0 之後默認是沒法實現的,緣由在於.NET Framework 2.0 之後 Assembly.Load 方法有緩存,第一次加載目標程序集的失敗或者成功的狀態都會被緩存,這樣在你下一次加載目標程序集時不會真的加載,會直接從緩存裏取目標程序集的內容和狀態。

對這種狀況咱們能夠在調用Assembly.Load 方法的宿主程序的app.config 中加入以下配置信息來禁用緩存:

複製代碼
<?xml version="1.0"?>
<configuration>
  <runtime>
    <disableCachingBindingFailures enabled="1" />
  </runtime>
  <startup>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>
複製代碼

 

4. 還有其餘方案嗎?

CLR 框架略圖:

CLR

由於目前Assembly 的加載與卸載是徹底由應用程序域控制的,一個程序集只能夠經過應用程序域加載,只能經過應用程序域的卸載而卸載,其餘任何方式都不能夠!!!

那麼咱們能夠在須要時將目標程序集加載進應用程序域,不須要時將應用程序域卸載,可是當前應用程序域的卸載只能經過關閉程序來實現。

到目前爲止,看似無解了,可是咱們忽略了一個事實,那就是咱們能夠在當前應用程序域中建立子應用程序域。能夠經過在子應用程序域中進行程序集的加載,或者說只要涉及程序集加載的所有放在子應用程序域中,主應用程序域中不作任何與程序集加載有關的事情。

這部份內容我就不詳細介紹了,你們能夠參考:

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

 

5. 咱們確實須要使用Assembly.Load嗎?

由於Assembly.Load 是將整個程序集以及其相關的依賴程序集所有加載進來,只要有一個出錯就會致使加載失敗。若是咱們只是爲了使用當前程序集的類型,而不是使用其方法或者屬性的話就徹底能夠拋棄Assembly.Load 方法。

微軟在.Net Framework 2.0 時介紹了幾個新的程序集加載APIs:

Assembly.ReflectionOnlyLoadFrom(String assemblyFile)

Assembly.ReflectionOnlyLoad(byte[] rawAssembly)

Assembly.ReflectionOnlyLoad(String assemblyName)

基於開篇提到的Assembly.Load 方法的5種問題, Reflection Only程序集加載APIs 能夠:

  1. 跳過程序集強命名認證。
  2. 跳過程序集CAS策略認證。
  3. 跳過處理器架構檢查規則。
  4. 不在目標程序中執行任何方法,包括構造函數。
  5. 不該用任何綁定策略。

使用時有以下幾個注意事項:

1) CLR 不會搜索目標程序集所依賴的程序集,咱們必須經過ReflectionOnlyAssemblyResolve(Assembly.Load 中的對應事件是AssemblyResolve)事件手動處理。

2) 不能夠在經過ReflectionOnly 方法加載進來的程序集執行任何方法,包括構造函數,只能夠獲取程序集的信息和類型。

3) 建議使用Assembly.ReflectionOnlyLoadFrom 方法,可是若是目標程序集在GAC中那麼可使用Assembly.ReflectionOnlyLoad方法。

具體示例代碼以下:

複製代碼
using System;
using System.IO;
using System.Reflection;

public class ReflectionOnlyLoadTest
{
    private String m_rootAssembly;
    public ReflectionOnlyLoadTest(String rootAssembly)
    {
        m_rootAssembly = rootAssembly;
    }

    public static void Main(String[] args)
    {
        if (args.Length != 1)
        {
            Console.WriteLine("Usage: Test assemblyPath");
            return;
        }

        try
        {
            ReflectionOnlyLoadTest rolt = new ReflectionOnlyLoadTest(args[0]);
            rolt.Run();
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception: {0}!!!", e.Message);
        }
    }

    internal void Run()
    {
        AppDomain curDomain = AppDomain.CurrentDomain;
        curDomain.ReflectionOnlyAssemblyResolve += 
            new ResolveEventHandler(MyReflectionOnlyResolveEventHandler);
        Assembly asm = Assembly.ReflectionOnlyLoadFrom(m_rootAssembly);
        // force loading all the dependencies
        Type[] types = asm.GetTypes();
        // show reflection only assemblies in current appdomain
        Console.WriteLine("------------- Inspection Context --------------");
        foreach (Assembly a in curDomain.ReflectionOnlyGetAssemblies())
        {
            Console.WriteLine("Assembly Location: {0}", a.Location);
            Console.WriteLine("Assembly Name: {0}", a.FullName);
            Console.WriteLine();
        }
    }

    private Assembly MyReflectionOnlyResolveEventHandler(object sender, ResolveEventArgs args)
    {
        AssemblyName name = new AssemblyName(args.Name);
        String asmToCheck = Path.GetDirectoryName(m_rootAssembly) + "\\" + name.Name + ".dll";
        if (File.Exists(asmToCheck))
        {
            return Assembly.ReflectionOnlyLoadFrom(asmToCheck);
        }

        return Assembly.ReflectionOnlyLoad(args.Name);
    }
}
複製代碼

 

6. 爲何沒有Assembly.UnLoad 方法?

如下是CLR 產品單元經理(Unit Manager) Jason Zander 文章中的內容的整理:

  1) 爲了保證 CLR 中代碼所引用的代碼地址都是有效的,必須跟蹤諸如 GC 對象和 COM CCW 之類的特殊應用。不然會出現 Unload 一個 Assembly 後,還有 CLR 對象或 COM 組件使用到這個 Assembly 的代碼或數據地址,進而致使訪問異常。爲了不這種錯誤進行的跟蹤,目前是在 AppDomain 一級進行的,若是要加入 Assembly.Unload 支持,則跟蹤的粒度必須降到 Assembly 一級,這雖然在技術上不是不能實現,但代價太大了。   2) 若是支持 Assembly.Unload 則必須跟蹤每一個 Assembly 的代碼使用到的句柄和對現有託管代碼的引用。例如如今 JITer 在編譯方法時,生成代碼都在一個統一的區域,若是要支持卸載 Assembly 則必須對每一個 Assembly 都進行獨立編譯。此外還有一些相似的資源使用問題,若是要分離跟蹤技術上雖然可行,但代價較大,特別是在諸如 WinCE 這類資源有限的系統上問題比較明顯。   3) CLR 中支持跨 AppDomain 的 Assembly 載入優化,也就是 domain neutral 的優化,使得多個 AppDomain 能夠共享一份代碼,加快載入速度。而目前 v1.0 和 v1.1 沒法處理卸載 domain neutral 類型代碼。這也致使實現 Assembly.Unload 完整語義的困難性。

詳細請參考: http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

 

7. 須要牢記的經驗

1) 只加載本身須要直接調用的程序集,不加載目標程序集內部引用的程序集和其餘無關程序集。

2) 能使用RefelectionLoad 方法加載的程序集毫不要使用Assembly.Load 方法加載。

3) 一旦出現加載錯誤,不要顯而易見認爲是程序集不存在!要檢查程序集加載緩存、是否出現同一程序集被不一樣應用程序域加載狀況等。

 

至此,咱們已經闡述了Assembly.Load 方法的一些特性,你已經瞭解它了嗎?

 

參考連接:

http://msdn.microsoft.com/en-us/library/t07a3dye(v=vs.71).aspx

http://blogs.msdn.com/b/junfeng/archive/2004/11/03/252033.aspx

http://blogs.msdn.com/b/junfeng/archive/2004/08/24/219691.aspx

http://www.sosuo8.com/article/show.asp?id=2979

http://msdn.microsoft.com/en-us/library/ms404279.aspx

http://blog.csdn.net/xt_xiaotian/article/details/6362450

http://blogs.msdn.com/b/jasonz/archive/2004/05/31/145105.aspx

http://www.cnblogs.com/ccBoy/archive/2004/07/13/23636.html

http://www.cnblogs.com/wayfarer/archive/2004/09/29/47896.html

http://www.codeproject.com/Articles/42312/Loading-Assemblies-in-Separate-Directories-Into-a

http://msdn.microsoft.com/en-us/library/ms404312.aspx

相關文章
相關標籤/搜索