C#将dll嵌入exe

经过摸索,其实还有更多的方法实现嵌入,以下是来自stackoverflow答案

https://stackoverflow.com/questions/9228423/how-to-use-an-dll-load-from-embed-resource

总结下:

1.使用微软的ilmerge(免费)

2.使用smartAssembly(收费)

3.如下手动添加代码


文章主要参考了下面文章,我在这里做一个小精简总结:

1.将要嵌入的第三方dll文件,拷贝到项目根目录,比如下图中的ICSarpCodeSharpZipLib.dll


2.在dll属性中设置成“嵌入的资源”


3.在program.cs中添加代码,如下图


复制粘贴代码如下:

using System.Reflection;

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
	string resourceName = new AssemblyName(args.Name).Name;
	resourceName = Assembly.GetExecutiongAssembly().GetManifestResourceNames().First(x => x.Contains(resourceName));
	using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
	{
		byte[] assemblyData = new byte[stream.Length];
		stream.Read(assemblyData, 0, assemblyData.Length);
		return Assembly.Load(assemblyData);
	}
}

4.生成exe,输出的文件会包含exe和dll,只需要将exe拷贝出来即可

(如果不想输出dll,在引用中将dll设置成不复制到本地,如下图)


特别注意:

如果采用以上步骤嵌入dll文件,在运行的时候报错,找不到dll,最有可能的原因是

string resourceName = new AssemblyName(args.Name).Name + ".dll";

中resourceName不正确,导致Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)返回null

排查方法:

在CurrentDomain_AssemblyResolve中打印出实际需要的dll名称

System.Diagnostics.Debug.WriteLine(Assembly.GetExecutiongAssembly().GetManifestResourceNames());

对比resourceName是否正确

因此,可以修改resourceName的代码

string resourceName = new AssemblyName(args.Name).Name;

resourceName = Assembly.GetExecutiongAssembly().GetManifestResourceNames().First(x => x.Contains(resourceName));


---------------------------以下为转载-------------------

最近用WinForm开发一个小程序,其中用到了HtmlAgilityPack(用于解析html)和Newtonsoft.Json(用于解析json)这两个库,由于编译后生成的exe还必须带上这两个dll,非常不爽,于是就上网寻求将其嵌入exe的方法,经过一番折腾,总算有了结果。

总结起来,找到的方案有如下几个:

  1. 使用ILMerge等工具将dll嵌入exe中
  2. 将dll内嵌于exe中,并在首次运行时将其释放出来
  3. 将dll内嵌于exe中,并在使用到dll时,将其加载在内存中

对于1,经过初步了解,应该是要生成exe后,通过该工具将dll嵌入。这样每次修改程序都要重复这个操作,感觉太繁琐,所以直接跳过,寻找别的方案(当然也可能是我不够了解带来的误解,如果有了解的同学欢迎指出);对于2,虽然可以满足分发的时候只发布一个exe文件,但一旦运行起来又甩出一堆dll,看着还是不爽;3就是我采纳的方案,下面细说。

一、将dll文件加入工程

1.找到dll文件

可以在项目的“引用”下找到dll,并且根据属性中的“路径”找到文件所在目录;也可以使用bin目录下自动生成(其实就是复制)的dll文件 
这里写图片描述 
这里需要注意,“引用”下的dll,需要设置“复制本地”为False,这样在bin目录下生成exe的时候就不会顺便复制dll了(感觉这步可要可不要)。

2.加入工程

可以对着项目(我的即“ShadowSuHelper”)右键->添加->现有项;或者更简单的,复制1中找到的dll,直接在工程中粘贴 
这里写图片描述 
加入dll后,工程结构如上图,引用中的还需要保留(否则代码编译不通过);此外.dll文件也是必须的(运行时需要调用)。最后,别忘了对刚加入的两个dll属性中“生成操作”改为“嵌入的资源”,这样生成的exe就会嵌入这两个dll,体积显著增大,哈哈。

二、添加AssemblyResolve处理函数

添加了AssemblyResolve的处理函数后,当调用dll找不到时,就会回调该函数,我们只需要在这个函数中,将嵌入exe的dll获取并解析返回即可。这个事件必须在调用dll之前添加,否则就没用了,这里我们选择在Program.cs的Main()方法中设置。 
首先引入命名空间 
using System.Reflection; 
然后在Main方法开头加入 
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

其中的CurrentDomain_AssemblyResolve就是我们的回调函数了,完整代码如下:

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string resourceName = 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);
    }
}

可以在CurrentDomain_AssemblyResolve中设置断点查看,会发现当程序中首次调用到HtmlAgilityPack.dll或者Newtonsoft.Json.dll的方法时会回调该函数,并且不会再报找不到dll的错误了。

三、其他

1.经过测试,一旦dll被加载到内存中,就会一直存在。所以只有首次找不到dll时会去加载,其他时候就不用了,所以感觉《Load DLL From Embedded Resource 》这篇文章中的提前加载(如EmbeddedAssembly.Load(resource1, "System.Windows.Forms.Ribbon35.dll");)和缓存方式(static Dictionary<string, Assembly> dic = null;)应该没什么必要。 
2.要嵌入非托管dll,可以参考底部提到的《Load DLL From Embedded Resource 》这篇文章,这里给出从中摘抄的一段话:

Unmanaged DLL cannot be loaded directly from stream/byte[] as Managed DLL do.

1.First load the DLL from Embedded Resource into byte[]. 
2.Write byte[] into a physical file and stored it at temp folder. Use 
3.Assembly.LoadFile() to load the file into memory.

参考文章

[C#.NET] 合併 .net 組件 / 將 .dll 嵌入 .exe 
Load DLL From Embedded Resource 
.Net平台实现嵌入DLL C#描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值