写 Winform 或 WPF 程序用到第三方 dll,打包程序时就不能简单把 exe 拷出来,要不制作安装包(对于简单的程序来说麻烦),要不将调用的第三方 dll 嵌入到 exe 中,说到这就有坑了。
前面的步骤大同小异,将调用的 dll 添加到项目中,并将 dll 的 “属性”->“生成操作”更改为“嵌入的资源”,再添加引用即可。
vs2019 16.5.3 之前版本我一直是这么写的:
Winform(Program.cs):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = "ECC_Crypt.dll." + new AssemblyName(args.Name).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);
}
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
|
WPF(App.xaml.cs):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
protected override void OnStartup(StartupEventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = "SM4.dll." + new AssemblyName(args.Name).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);
}
};
base.OnStartup(e);
}
|
其中 Winform 的 ECC_Crypt 为解决方案的名称,dll 为手动添加用来存放 dll 的文件夹(WPF 同理)。经过如上操作,生成的 exe 是可以拷贝至其他目录执行而不会出现找不到 dll 错误的情况。
但是 vs2019 更新至 16.5.3(之后的版本应该也一样) 之后,用上面的方法就会报错:System.NullReferenceException:“未将对象引用设置到对象的实例。” 调试后发现 var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName) 中 stream 为 null 导致异常,猜测是 vs2019 检查异常变严格了。
将代码更改为如下即可编译通过:
Winform:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string dllName = new AssemblyName(args.Name).Name + ".dll";
var assm = Assembly.GetExecutingAssembly();
var resourceName = assm.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null)
{
return null;
}
using (var stream = assm.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
|
WPF:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
protected override void OnStartup(StartupEventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string dllName = new AssemblyName(args.Name).Name + ".dll";
var assm = Assembly.GetExecutingAssembly();
var resourceName = assm.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null)
{
return null;
}
using (var stream = assm.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
base.OnStartup(e);
}
|