在有这个需求时,在网上搜索了许多这方面的资料,结果是网上的方法并不适用我的项目。其实网上的方法是对的,但由于未考虑一些问题,所以导致在我的项目不成功。现将我优化过的方法提供如下【不明白看代码注释】:
首先说明个问题,C#动态加载dll,也即内存加载dll,通常包括两种情况,加载C++的dll和C#的dll,本文阐述的是后者。
// 综述:本例中,我要通过内存加载的程序集是x4lib,并使用其中的一个类ProcPiper。
// 把握要点:尽可能的让CurrentDomain_AssemblyResolve在程序启动的最初就被执行。
// 代码不多,请耐心看,理解之,欲速则不达
static class Program
{
// 1.此函数会在Main执行前执行,这样使得dll加载事件尽可能在程序启动的最初就被注册;
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
// 2.网上的方案在我这个项目行不通正是因为这行代码,因为我声明了一个全局静态且包含在被嵌入的dll中的一个类对象。解决方法看2'
// public static x4lib.ProcPiper _Piper = null;
// 2'.解决方法:将类型x4lib.ProcPiper改为object,并设置为属性方式;
private static object _Piper = null;
public static x4lib.ProcPiper Piper
{
get { return (x4lib.ProcPiper)_Piper; }
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Program._Piper = new x4lib.ProcPiper();
// 启动C#端ProcPiper
new Thread(new ThreadStart(delegate()
{
Program.Piper.Exec(1024);
})).Start();
Application.Run(form1);
Program.Piper.Exit();
}
private static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// 使用方法:
// 1.将dll通过“添加引用”加到当前项目;
// 2.将dll添加为资源文件,同时设为“嵌入的资源”;
// 3.通过事件的方式,使程序在Main执行前触发此事件;
// 注意事项:
// 1.不可以在当前项目直接定义被嵌入的dll中的静态全局对象
string strNameSpace = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
string strResources = "Resources";
string strPrefix = strNameSpace + "." + strResources + ".";
string strCSharpDll = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
Assembly assembly = Assembly.GetExecutingAssembly();
string[] strRess = assembly.GetManifestResourceNames();
foreach (string it in strRess)
{
string strRes = it.Substring(strPrefix.Length);
if (strRes.Equals(strCSharpDll))
{
using (var tmp = assembly.GetManifestResourceStream(it))
{
byte[] buf = new byte[tmp.Length];
tmp.Read(buf, 0, (int)tmp.Length);
return Assembly.Load(buf);
}
}
}
return null;
}
}