需求描述

在C#的项目中,经常会引用一些外部的DLL库。在发布的时候,必需将这些库和可执行程序一起发布,否则会导致程序无法运行。在一些项目中,会有这样的需求:希望发布一个单独的可执行文件,将所引用的DLL文件打包入EXE程序中的方式来实现。

实现原理

想要实现这个库有以下三种方式:

  1. 以嵌入的资源将DLL文件打包,然后在运行的时候再释放出来;
  2. 以嵌入的资源将DLL文件打包,然后在运行时直接加载至内存;
  3. 其他,比如使用第三方工具打包(比如 这里),或者将DLL单独发布(比如 这里)等。

针对不同的需求可以选择以上三种情况的任意一种。这里介绍第1种和第2种两种方法。在介绍之前,首先了解一下什么是嵌入的资源。

嵌入的资源

在C#的项目中,我们可以添加任意类型的文件,作为一个纯二进制的文件嵌入至项目中,作为资源使用。以下对嵌入资源的添加和使用进行简要说明。

  • 添加嵌入资源
    在项目上中点击右键,选择“添加->现有项”,然后添加所需要的文件。如果要添加任意类型,在弹出的打开对话框中选择“所有文件(*.*)”。然后选中已经添加的文件,在属性窗口中设置“生成操作”的属性为“嵌入的资源”。
  • 嵌入资源的路径
    已经添加的资源文件,使用绝对路径访问,格式为 [解决方案名称].[文件夹路径].[文件名]。比如在一个名为 Test 的项目中,加入的资源文件 a.dll 在根目录下,则访问路径为 Test.a.dll。如果 a.dll 是放在文件夹下,比如 Resource 目录下,则其路径为 Test.Resource.a.dll,以此类推。
  • 使用嵌入资源
    在C#中,一个DLL即是一个程序集,使用 System.Reflection.Assembly 类来表示。加载一个程序集可以使用 Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath) 来实现,其中 resoucePath 即为DLL的路径。详细操作可参见相关文章。

注意:以上的嵌入资源都是托管的DLL,即是使用.NET创建的类库。如果是非托管的DLL,如使用C++建立的,则不管用。如需要加载非托管DLL,可以使用以下办法:
1)从嵌入的DLL加载字节数据 data;
2)将data写至一个临时文件中;
3)使用 Assembly.LoadFile(path) 进行加载即可。

实现过程

方法1
第1种方法,只需要读取嵌入的资源,然后写到本地即可。以下代码展示了如何从EXE中读取指定的嵌入的资源文件,读取的结果为字节数组,即 byte[] 类型。

public byte[] GetResource(String respath)
{
	System.IO.Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test.a.dll");
	byte[] data = new byte[stream.Length];
	stream.Read(data, 0, data.Length);
	return data;
}

读取成功后的数据可以直接写回到磁盘保存为dll文件,然后程序即可正常调用。比如,我们可以使用使用 System.IO.File.WriteAllBytes("a.dll", data) 方法来保存读取至的程序目录下,名称为 a.dll

方法2
第2种方法,可参见 此文,以下为以加载 Test.a.dll 的示例代码。

static void Main(string[] args)
{
	// 在整个程序最开始的地方进行引用DLL的内存载入
	AppDomain.CurrentDomain.AssemblyResolve += (s, a) =>
	{ 
		using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Test.a.dll"))
		{
			byte[] data = new byte[stream.Length];
			stream.Read(data, 0, data.Length);
			return Assembly.Load(data);
		}
	};
}

小结

本文介绍了两种将DLL嵌入至EXE,从而实现一个独立的EXE程序的实现办法。这两种办法都是通过嵌入的资源的方式将DLL文件嵌入在EXE中。不同之处在于,第1种方法将DLL文件在启动时读取出来,然后保存在本地,从而实现和一般C#项目一样的项目结构(即EXE+DLL的结构)。第2种方式则更为简单,直接加载至内存,从而在运行时,也不需要本地的DLL文件。具体使用,可以根据实际需求来决定。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐