上篇文章 《(6)程序集加載上下文》 已經告訴了我們各種程序集上下文,現在來看看.Net中是如何支持這些上下文的。
一、Assembly類提供的多個載入動態程序集方法
1. Load(),LoadFile(),LoadFrom(),LoadWithPartialName(),ReflectionOnlyLoad(),ReflectionOnlyLoadFrom(),UnsafeLoadFrom()
1) Load()
可通過程序集的唯一標識符AssemblyName對象或程序集的長|短格式名稱字符串或基於通用對象文件格式(COFF,Common Object File Format)的映像byte[] 來加載程序集。
a) 實現在加載目標程序集時不鎖定它
byte[] buffer = System.IO.File.ReadAllBytes(ass.Location);
Assembly assembly = Assembly.Load(buffer);
暫時先將目標程序集鎖定,然后把程序集內容復制到內存中,讀取后將程序集解鎖並從內存中加載目標程序集的拷貝。
2) LoadWithPartialName() 已過時,使用Load()代替。
使用部分名稱從應用程序目錄或從全局程序集緩存加載程序集。注意:此方法加載不到程序集返回null,不會報錯。
3) LoadFile()
加載指定路徑上的程序集文件的內容。
4) LoadFrom()
可通過給定程序集文件名或路徑、哈希值及哈希算法來加載程序集。
5) ReflectionOnlyLoad()
可用程序集的長|短格式名稱字符串或基於通用對象文件格式(COFF)的映像來加載程序集。程序集被加載到調用方的應用程序域的只反射上下文中。
6) ReflectionOnlyLoadFrom()
將給定路徑的程序集加載到只反射上下文中。
7) UnsafeLoadFrom()
使用包含程序集清單的文件的名稱或路徑。繞過某些安全檢查,將程序集加載到加載源上下文中。
8) 差異
a) Load(string|AssenblyName)\LoadFrom()\LoadWithPartialName()\ReflectionOnlyLoad()\ReflectionOnlyLoadFrom()\UnsafeLoadFrom()
i. Load(string|AssemblyName)\LoadWithPartialName()\ReflectionOnlyLoad() 使用程序集(長|短)名稱加載。
ii. LoadFrom()\ReflectionOnlyLoadFrom()\UnsafeLoadFrom() 使用程序集文件名或路徑加載。(注意有后綴如 .dll )
iii. 內部實現會先獲取到AssemblyName,調用InternalLoadAssemblyName來加載程序集,InternalLoadAssemblyName若有傳入Evidence參數的就進行Evidence憑據驗證;然后再檢查路徑訪問權限(除了UnsafeLoadFrom()):根據程序集標識探測路徑或傳入路徑參數來判斷是否為本地文件(file://開頭的URI),然后進行相應的本地文件訪問權限(FileIOPermission)或Internal資源訪問權限(WebPermission)判斷,再調用 extern RuntimeAssembly _nLoad() 進行程序集加載。
(不支持文件傳輸協議 (FTP)。如果為 assemblyFile 提供的 URI 是一個 FTP 地址,則不會加載程序集。不引發異常。)
iv. 在.NET Framework 2.0 以后比如 Assembly.Load 方法有緩存,第一次加載目標程序集的失敗或者成功的狀態都會被緩存,這樣在你下一次加載目標程序集時會直接從緩存里取目標程序集的內容和狀態。
當失敗后我們可能想要再加載一次或者加載多次直到成功為止,這時需要在app.config 中加入如下配置信息來禁用緩存:
<runtime> <disableCachingBindingFailures enabled="1" /> </runtime>
注意: ReflectionOnlyLoad()\ReflectionOnlyLoadFrom() 只反射加載不會使用緩存。
v. ReflectionOnlyLoad()\ReflectionOnlyLoadFrom()
1) 兩個方法內部調用同Load()\LoadFrom(),只是在調用加載內部方法時“只反射方法”傳入bool參數introspection(檢查)為true。
2) 這兩個方法是將程序集載入到“只反射上下文”,所以不可以在通過ReflectionOnly 方法加載進來的程序集執行任何方法,包括構造函數,只可以獲取程序集的信息和類型。可以使用Assembly實例的ReflectionOnly屬性確定該程序集是否加載到了只反射上下文中。
3) 這兩個方法在載入程序集時不會將依賴程序集載入,需自行處理ReflectionOnlyAssemblyResolve事件來自行加載依賴程序集;(ReflectionOnlyLoad(byte[]) 重載也一樣)
4) 加載到只反射上下文中的代碼無法執行。這意味着不能創建自定義特性的實例,因為這將需要執行其構造函數。若要在只反射上下文中加載和檢查自定義特性,請使用 CustomAttributeData 類。可以通過使用靜態 CustomAttributeData.GetCustomAttributes 方法的相應重載獲取此類的實例。
vi. LoadFrom()
在LoadFrom載入一個程序集時,會先檢查前面是否已經載入過具有相同長名稱的程序集(AssemblyName.FullName)----注意程序集內容不一樣但FullName相同后面進行的載入會直接取前面載入的緩存。
可以通過LoadFile()方法加載不同路徑擁有相同AssemblyName.FullName的多個程序集。
另外:ReflectionOnlyLoadFrom()此方法載入相同AssemblyName.FillName程序集時會報錯,eg:API 限制: 程序集“file:///C:\新建文件夾\ClassLibrary123.dll”已從其他位置加載。無法從同一個 Appdomain 中的另一新位置加載該程序集。
b) LoadFile(path)
調用 extern RuntimeAssembly nLoadFile() 進行程序集加載,不會加載程序的依賴項。
c) Load(byte[])\ReflectionOnlyLoad(byte[])
調用 extern RuntimeAssembly nLoadImage() 進行程序集加載。
2. 動態加載程序集使用證據有兩種截然不同的方式:
a) 將輸入證據與加載程序所收集的證據合並,以創建用於策略決策的最終證據集。使用傳入 Evidence 證據對象的方法包括 Assembly.Load、Assembly.LoadFrom 和 Activator.CreateInstance。
b) 原封不動地使用輸入證據作為用於策略決策的最終證據集。使用這種語義的方法包括 Assembly.Load(byte[]) 和 AppDomain.DefineDynamicAssembly()。
在加載時,程序集的證據用作安全策略的輸入。安全策略是由企業和計算機的管理員以及用戶策略設置建立的,它在執行時確定向所有托管代碼授予的權限組。可以為該程序集(如果該程序集具有簽名工具生成的簽名)的發行者建立安全策略,或者為該程序集的下載網站和區域(就 Internet Explorer 而言)建立安全策略,也可以為該程序集的強名稱建立該策略。例如,一台計算機的管理員可以建立這樣一種安全策略:它允許從某一網站下載由指定軟件公司簽發用以訪問計算機上的數據庫的所有代碼,但不授予對該計算機磁盤的寫訪問權。
二、使用Fuslogvw.exe程序集綁定日志查看器顯示程序集綁定的詳細信息,優化性能
啟動:
您必須具有管理員權限運行 fuslogvw.exe。
1、如果您在計算機上安裝的 Visual Studio: 的任務欄上,單擊Start,單擊All Programs,單擊Visual Studio,單擊Visual Studio Tools,用鼠標右鍵單擊Visual Studio Command Prompt,然后單擊以管理員身份運行在快捷菜單中。
- 或 -
2、如果您必須在計算機上安裝 Windows SDK: 的任務欄上,單擊Start,單擊All Programs,為 Windows SDK 中單擊該文件夾,用鼠標右鍵單擊Command Prompt (或CMD Shell),然后單擊以管理員身份運行在快捷菜單中。
在管理員命令提示符下鍵入命令:fuslogvw
首先,按如下設置就可跟蹤程序集綁定的詳細信息。
幾個跟蹤示例:
1. 使用 Load() 長名稱加載程序集
注意長名稱和短名稱加載程序集探測中對策略文件的影響
Eg:
使用長名稱加載程序集並設置<codeBase>如下圖,正確應用了配置策略
因為在使用如< assemblyIdentity >、< codeBase >等元素需要提供版本,公鑰等信息,所以使用短名稱不會應用配置策略。
2. 通過執行兩次失敗加載操作,可以驗證下面方法在探測程序集時會使用已經加載過的緩存內容和狀態
Load(string|AssenblyName) ,LoadWithPartialName(),LoadFrom(),UnsafeLoadFrom()
3. LoadFile() 加載程序集過程
如果根據指定路徑綁定成功,則在“沒有上下文”的情況下進行加載程序集;【如果成功加載】,那么CLR還將嘗試根據加載到的程序集長名稱標識調用 Load() 將此程序集同時加載到“默認加載上下文”中。
這邊的兩次綁定不是切換,所以為了避免出現類型標識問題(即:將同一個程序集加載到不同上下文中),盡量避免使用該方法。
4. Load(byte[]) 加載程序集綁定信息
使用 byte[] 內存進行程序集加載的不會產生程序集綁定日志。(如果加載到的程序集標識(在應用策略后創建的)與全局程序集緩存中的程序集標識匹配,將會從全局程序集緩存加載程序集,產生程序集綁定信息,這時只加載到“默認加載上下文”中)
5. LoadFrom()\UnsafeLoadFrom()
1) 使用文件名綁定(不是具體路徑)
先使用文件名獲取到程序集信息,然后根據此程序集的長名稱標識調用 Load() 將此加載從“加載位置上下文”切換到“默認加載上下文”。
ReflectionOnlyLoadFrom() 使用文件名綁定時,不會發生上面的切換操作,直接加在到“加載上下文”中;使用具體路徑時,不會在GAC和其他位置搜索文件,只探測指定路徑位置的程序集。
6. Load()\LoadWithPartialName() 執行部分綁定信息綁定過程
先使用部分綁定信息獲取到程序集信息,然后根據此程序集的長名稱標識再調用Load()方法執行加載程序集。
使用ReflectionOnlyLoad()方法不會應用版本策略,所以你指定的是哪個版本,獲得的就是哪個版本。如果要自行為一個程序集標識指定版本控制策略,可將字符串傳給AppDomain的ApplyPolicy()。
所以在執行程序集加載時:
1) 盡量使用程序集長名稱或完整路徑進行加載,以避免重復探測程序集開銷。
2) 使用 LoadFile() 要注意避免不要將同一程序集加載到不同上下文中。
3) 對於只需獲取程序集信息而不通過反射操作成員時,要使用“只反射加載”
《反射機制》系列:
參考資源: