一、場景
- 開發小工具時需引用DLL,而部署分發時只希望提供執行文件。
- 在項目架構上隱藏某些DLL文件(文件結構層面)
二、原理
- DLL的加載是在使用時才被加載,而不是啟動時(卸載是由垃圾回收器處理,無法預測回收時間)。
- AppDomain.CurrentDomain.AssemblyResolve事件可用於.Net框架加載程序集失敗時手動控制重新加載程序集。
- DLL文件可作為資源內嵌進程序集。
三、實現要點
- 將DLL以“嵌入的資源”的形式加入項目。
- 引用DLL,並設置為“不復制到本地”。
- 在Main方法內利用AppDomain.CurrentDomain.AssemblyResolve事件手動控制加載DLL(也可在任意使用DLL前能夠被執行到的地方訂閱事件)。
- 保證在加載DLL成功前不以任何形式引用此DLL。
- 可同時實現對多個DLL及其他程序集進行加載。
四、示例代碼
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 // 引入加載dll所需命名空間 7 using System.Reflection; 8 9 //using DllDemo; 10 // 錯誤,不要用任何方式調用dll,會導致未加載dll就依賴dll 11 12 namespace AppDemo 13 { 14 class Program 15 { 16 static void Main(string[] args) 17 { 18 // 框架加載dll失敗后執行,手動加載dll 19 AppDomain.CurrentDomain.AssemblyResolve += (sender, senderArgs) => 20 { 21 // 當前程序集 22 var executingAssembly = Assembly.GetExecutingAssembly(); 23 // 當前程序集名稱 24 var assemblyName = new AssemblyName(executingAssembly.FullName).Name; 25 // dll名稱 26 var dllName = new AssemblyName(senderArgs.Name).Name; 27 // 待加載dll路徑,指向當前程序集資源文件中dll路徑。* 根據程序結構調整,使其正確指向dll 28 var dllUri = assemblyName + "." + dllName + ".dll"; 29 // 加載dll 30 using (var resourceStream = executingAssembly.GetManifestResourceStream(dllUri)) 31 { 32 var assemblyData = new Byte[resourceStream.Length]; 33 resourceStream.Read(assemblyData, 0, assemblyData.Length); 34 return Assembly.Load(assemblyData); //加載dll 35 } 36 }; 37 // app入口 38 AppDemo.Demo(); 39 //var obj = new DllDemo.Dll(); 40 //錯誤,不要用任何方式調用dll,會導致未加載dll就依賴dll 41 Console.Read(); 42 } 43 } 44 }
演示源碼下載: http://files.cnblogs.com/kui2/DEMO-EmbeddedDllDemo.zip
五、另一個想法
將DLL等資源文件內嵌程序集,執行時將其釋放到磁盤上恢復所需文件。恩,費力不討好而且容易被安全軟件盯上的辦法。非常時期可能會有用吧。