第一节:程序集加载
我們知道JIT編譯器將方法的IL代碼編譯成本地代碼時,會查看IL代碼中引用了哪些類型。在運行時,JIT編譯器利用程序集的TypeRef和AssemblyRef元數據表來確定哪一個程序集定義了所引用的類型。在AssemblyRef元數據表的記錄項中,包含了構成程序集強名稱的各個部分。JIT編譯器獲取所有這些部分,包括名稱(無擴展名和路徑)、版本、語言文化和公鑰標記,并把它連接成一個字符串。然后,JIT編譯器嘗試將與該標識匹配的一個程序集加載到AppDomain中(如果還沒有加載的話)。如果被加載的程序集時弱命名的,那么標識中只包含程序集的名稱(不包含版本、語言文化以及公鑰標記信息)。
在內部,CLR使用System.Reflection.Assembly類的靜態方法Load來嘗試加載這個程序集。這個方法在.NET Framework SDK文檔中是公開的,可調用它顯式的將一個程序集加載到AppDomain中。這個方法是CLR中的與Win 32 LoadLibrary函數等價的方法。Assembly的Load方法實際上有幾個重載版本。以下是最常用的重載版本的原型:
public static Assembly Load(AssemblyName assemblyRef);
public static Assembly Load(string assemblyString);
在內部,Load導致CLR向程序集應用一個版本綁定重定向策略,并在GAC(全局程序集緩存)中查找程序集。如果沒找到,就接著去應用程序的基目錄、私有路徑子目錄和codebase位置查找。如果調用Load時,傳遞時一個弱命名程序集,Load就不會向程序集應用一個版本綁定重定向策略,CLR也不會去GAC中查找程序集。如果Load找到程序集,會返回對代表已加載的那個程序集的一個Assembly對象的引用。如果Load沒有找到指定的程序集,會拋出一個System.IO.FileNotFoundException。
重要提示:一些開發人員可能注意到,System.AppDomain提供了一個Load方法。和Assembly的Load方法不同,AppDomain的Load是一個實例方法,它允許將一個程序集加載到一個指定的AppDomain中。該方法設計供非托管代碼調用,允許宿主將一個程序集”注入”到一個AppDomain中。托管代碼的開發人員一般情況下不應調用它,因為在調用AppDomain的Load方法時,需要向它傳遞一個標識了程序集的字符串,該方法隨后會應用策略,并在一些常規位置搜索一些程序集。我們知道,AppDomain關聯了一些告訴CLR如何查找程序集的設置。為了加載這個程序集,CLR將使用與指定AppDomain關聯的設置,而不是與發出調用的那個AppDomain關聯的設置。
然后,AppDomain的Load方法會返回對程序集的一個引用System.Reflection.Assembly不是從MarshalByRefObject派生的,所以程序集對象必須按值封送回發出調用的那個AppDomain.但是,現在CLR就會用發出調用的那個AppDomain的設置來定位并加載程序集。如果使用發出調用的那個AppDomain的策略和搜索位置沒有找到指定的程序集,就會拋出一個FileNotFoundException。這個行為一般不是你所期望的,應該避免使用AppDomain的Load方法。
在大多數動態可擴展的應用程序中,Assembly的Load時程序集加載到AppDomain的首選方式。但是,它要求你實現掌握構成程序集標識的各個部分。開發人員經常要寫一些工具和實用程序,以便在程序集上執行一些處理。這些工具的例子包含:ILDasm.exe,PEVerify.exe,CorFlags.exe,GACUtil.exe, Sgen.exe,SN.exe,XSD.exe等,所有這些工具都要獲取引用一個程序集文件的路徑名(包括文件擴展名)的命令行實參。為了以指定路徑名的的方式加載一個程序集,要調用Assembly的LoadForm方法。在內部,LoadForm首先會調用System.Reflection.AssemblyName類的靜態方法GetAssemblyName,該方法打開指定的文件,查找AssemblyRef元數據表的記錄項,提取程序集標識信息,然后以一個System.Reflection.AssemblyName對象的形式返回這些信息(文件同時會關閉)。隨后,LoadForm方法在內部調用Assembly的Load方法,將AssemblyName對象傳給他。然后,CLR會應用版本綁定重定向策略,并在各個位置查找匹配的程序集,如果Load找到匹配的程序集,就會加載它,并返回代表已加載一個程序集的對象Assembly,LoadForm將返回這個值。如果Load沒有找到匹配的程序集,LoadForm就會加載通過LoadForm實參傳遞的路徑中的程序集。當然,如果以加載了一個具有相同標識的程序集,LoadFrom方法會簡單的返回代表已加載程序集的一個Assembly對象。
VS的UI設計人員和其他工具一般用的是Assembly的LoadFile方法。這個方法可以從任何路徑加載一個程序集,并可將具有相同標識的一個程序集多次加載到一個AppDomain中。在設計器/工具中對應用程序的UI進行了修改,CLR不會自動解析任何依懶性問題;你的代碼必須向AppDomain的AssemblyResolve事件登記,并讓事件回調方法顯示地加載任何依賴的程序集。
如果你構建的一個工具只是通過反射來分析程序集的元數據,并希望確保程序集中的任何代碼都不會執行,那么加載程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法。
ReflectionOnlyLoadFrom方法將加載由路徑指定的文件;文件的強名稱標識不會獲取,也不會在GAC和其他位置搜索。ReflectionOnlyLoad方法會在GAC、應用程序基目錄、私有路徑和codebase指定的位置搜索指定的程序集。但是,和Load方法不同的是,ReflectionOnlyLoad不會應用版本控制策略,所以你指定了哪個版本,獲得的就是哪個版本。如果要自行為一個程序集標識指定版本控制策略,可以將字符串傳給AppDomain的ApplyPolicy方法。
ReflectionOnlyLoadFrom 或者ReflectionOnlyLoad方法加載程序集時,CLR禁止程序集中的任何代碼執行,試圖執行由這兩個方法加載的程序集中的代碼,會導致CLR拋出一個異常,這兩個方法允許工具加載延遲簽名的程序集,這種程序集正常情況下會因為安全權限不夠而無法加載。另外,這種程序集也可能是為不同的CPU架構而創建的。
利用反射來分析由這兩個方法之一加載的程序集時,代碼經常需要向AppDomain的ReflectionOnlyAssemblyResovle事件注冊一個回調方法,以便手動加載任何引用的程序集;CLR不會自動幫你做這個事情,回調方法被調用時,它必須調用Assembly的ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法,以便顯示加載一個程序集,并返回對程序集的一個引用。
許多應用程序都由一個要依賴于眾多DLL文件的EXE文件組成。部署這個應用程序時,所有文件都必須部署。然后,有一個技術允許只部署一個EXE文件。首先,標識出EXE文件要依賴的、同時不是作為.NET FRAMEWORK本身的一部分發布的所有DLL文件,將這些DLL添加到你的VS項目中,對于添加的這些DLL,有顯示它的屬性,并將它的“生成操作”更改為”嵌入的資源”。這回導致C#編譯器將DLL文件嵌入EXE文件中,以后只需部署這個EXE文件即可。
在運行時,CLR會找不到依賴的DLL程序集,為了解決這個問題,應用程序初始化時,向AppDomain的ResolveAssembly事件登記一個回調方法,
?
?
AppDomain.CurrentDomain.AssemblyResolve += (sender, arg) =>{String resourceName = "AssemblyLoadingAndReflection:" + new AssemblyName(arg.Name) + ".dll";using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)){Byte[] assemblyData = new Byte[stream.Length];stream.Read(assemblyData, 0, assemblyData.Length);return Assembly.Load(assemblyData);}};
現在一個線程首次調用一個方法時,如果發現該方法引用了依賴的DLL文件中的一個類型,就會引發一個ResolveAssembly事件,而上訴回調代碼會找到所需的嵌入的DLL資源,并調用Assembly的Load方法重載版本,從而加載所需的資源。
?
轉載于:https://www.cnblogs.com/bingbinggui/p/4573487.html
總結
- 上一篇: reset.css
- 下一篇: java 泛型的几点备忘