C#工程项目经常需要引用外部DLL文件,在生成解决方案或者发布项目时,这些外部的DLL文件会被复制进Bin目录,生成的应用程序是一个多文件组成的结构。有时为了方便,我们想把引用的外部DLL文件编译进当前项目中,使得这个引用外部DLL文件的项目最后只生成一个可执行文件。过程如下:
准备工作:新建一个类库(作为被引用的外部DLL),编译成DLL文件,本例中的DLL文件名为TestExternalDll.dll。如果引用的DLL文件是NuGet的第三方插件,请跳过此步骤。
namespace TestExternalDll {
public class ExternalDll { //这里的修饰符不能是internal,否则引用它的其它项目将不能访问
public string GetString() {
return "External Dll String";
}
}
}
1.新建一个WinForm工程,将引入的外部DLL文件复制到该工程文件夹中(位置随便,我把它放到工程根目录下)。点选图中箭头指示处——“显示所有文件”,让之前复制的文件显示在工程项目文件夹中。
右键该文件,在弹出的菜单中选择“包括在项目中”。再右键该文件,在弹出菜单中选择“属性”,如图所示设置文件属性。
2.在解决方案管理器中右键点击“引用”,选择“添加引用”,在弹出窗口中选择“浏览”,在文件对话框中选定引用的DLL文件。
3.新建一个类文件LoadDll,以便在工程文件启动时加载引入的DLL文件,代码如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
namespace TestMain {
internal class LoadDll {
//已加载的DLL
private static readonly Dictionary<string, Assembly> LoadedDlls = new Dictionary<string, Assembly>();
//已处理的程序集
private static readonly Dictionary<string, object> Assemblies = new Dictionary<string, object>();
/// <summary> 在对程序集解释失败时触发 </summary>
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args) {
//获取加载失败的程序集的名称
string assName = new AssemblyName(args.Name).FullName;
//判断已加载的Dll集合中是否有已加载的同名程序集
if (LoadedDlls.TryGetValue(assName, out Assembly ass) && ass != null) {
LoadedDlls[assName] = null;
return ass;
} else {
//抛出加载失败的异常
throw new DllNotFoundException(assName);
}
}
/// <summary> 注册资源中的dll </summary>
public static void RegistDLL() {
//获取当前项目的程序集
Assembly ass = new StackTrace(0).GetFrame(1).GetMethod().Module.Assembly;
//如果已处理程序集列表中包含此程序集则返回,否则将此程序集加入到已处理程序集列表中(Assemblies)
if (Assemblies.ContainsKey(ass.FullName))
return;
else
Assemblies.Add(ass.FullName, null);
//绑定程序集加载失败事件
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
//获取所有资源文件文件名
string[] resources = ass.GetManifestResourceNames();
//DLL文件名的正则表达式*.dll,如果是其它扩展名,可以修改该正则表达式
var regex = new Regex("^.*\\.dll$", RegexOptions.IgnoreCase);
foreach (string res in resources) {
//如果是dll则加载
if (regex.IsMatch(res)) {
Stream s = ass.GetManifestResourceStream(res);
byte[] bytes = new byte[s.Length];
s.Read(bytes, 0, (int)s.Length);
Assembly loadedAss = Assembly.Load(bytes);
//判断是否已经加载
if (LoadedDlls.ContainsKey(loadedAss.FullName))
continue;
else
LoadedDlls[loadedAss.FullName] = loadedAss;
}
}
}
}
}
4.修改当前项目的Program.cs文件,将LoadDll类的RegistDLL()方法加入到启动Main函数中。
using System;
using System.Windows.Forms;
namespace TestMain {
internal static class Program {
/// <summary>应用程序的主入口点。 </summary>
[STAThread]
static void Main() {
LoadDll.RegistDLL(); //将引用的Dll文件加载到当前应用程序域中
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
5.在项目中随便调用引入的DLL文件中的对象和方法,生成解决方案,测试成功。
namespace TestMain {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
ExternalDll test=new TestExternalDll.ExternalDll();
this.Text = test.GetString();
}
}
}