写 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);
}