一、场景
1.开发小工具时需引用DLL,而部署分发时只希望提供执行文件。
2.在项目架构上隐藏某些DLL文件(文件结构层面)
二、原理
1.DLL的加载是在使用时才被加载,而不是启动时(卸载是由垃圾回收器处理,无法预测回收时间)。
2.AppDomain.CurrentDomain.AssemblyResolve事件可用于.Net框架加载程序集失败时手动控制重新加载程序集。
3.DLL文件可作为资源内嵌进程序集。
三、实现要点
1.将DLL以“嵌入的资源”的形式加入项目。
2.引用DLL,并设置为“不复制到本地”。
3.在Main方法内利用AppDomain.CurrentDomain.AssemblyResolve事件手动控制加载DLL(也可在任意使用DLL前能够被执行到的地方订阅事件)。
4.保证在加载DLL成功前不以任何形式引用此DLL。
5.可同时实现对多个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 }
五、另一个想法
将DLL等资源文件内嵌程序集,执行时将其释放到磁盘上恢复所需文件。恩,费力不讨好而且容易被安全软件盯上的办法。非常时期可能会有用吧。
六、参考
•conmajia 《C#化功大法——将DLL嵌入EXE》
•astonqa 《Winform 集成零散dll进exe的方法》