C# 动态装载 DLL
LoadDllTool.cs 如下:
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间 using System.Reflection; // 使用 Assembly 类需用此 命名空间 using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间 namespace CallDephiDll { public class LoadDllTool { //+------------------------------------------------------------------ //+ 动态装载 //+------------------------------------------------------------------ /// <summary> /// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递 /// </summary> public enum ModePass { ByValue = 0x0001, ByRef = 0x0002 } /// <summary> /// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName); /// </summary> /// <param name="lpFileName">DLL 文件名 </param> /// <returns> 函数库模块的句柄 </returns> [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); /// <summary> /// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName); /// </summary> /// <param name="hModule"> 包含需调用函数的函数库模块的句柄 </param> /// <param name="lpProcName"> 调用函数的名称 </param> /// <returns> 函数指针 </returns> [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); /// <summary> /// 原型是 : BOOL FreeLibrary(HMODULE hModule); /// </summary> /// <param name="hModule"> 需释放的函数库模块的句柄 </param> /// <returns> 是否已释放指定的 Dll</returns> [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)] static extern bool FreeLibrary(IntPtr hModule); /// <summary> /// Loadlibrary 返回的函数库模块的句柄 /// </summary> public static IntPtr hModule = IntPtr.Zero; /// <summary> /// GetProcAddress 返回的函数指针 /// </summary> private static IntPtr farProc = IntPtr.Zero; /// <summary> /// 装载 Dll /// </summary> /// <param name="lpFileName">DLL 文件名 </param> public static void LoadDll(string lpFileName) { hModule = LoadLibrary(lpFileName); if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); } //若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本: public static void LoadDll(IntPtr HMODULE) { if (HMODULE == IntPtr.Zero) throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 .")); hModule = HMODULE; } /// <summary> /// 获得函数指针 /// </summary> /// <param name="lpProcName"> 调用函数的名称 </param> public static void LoadFun(string lpProcName) { // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } /// <summary> /// 获得函数指针 /// </summary> /// <param name="lpFileName"> 包含需调用函数的 DLL 文件名 </param> /// <param name="lpProcName"> 调用函数的名称 </param> public static void LoadFun(string lpFileName, string lpProcName) { // 取得函数库模块的句柄 hModule = LoadLibrary(lpFileName); // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } /// <summary> /// 卸载 Dll /// </summary> public static void UnLoadDll() { FreeLibrary(hModule); hModule = IntPtr.Zero; farProc = IntPtr.Zero; } // Invoke方法的第一个版本: /// <summary> /// 调用所设定的函数 /// </summary> /// <param name="ObjArray_Parameter"> 实参 </param> /// <param name="TypeArray_ParameterType"> 实参类型 </param> /// <param name="ModePassArray_Parameter"> 实参传送方式 </param> /// <param name="Type_Return"> 返回类型 </param> /// <returns> 返回所调用函数的 object</returns> public static object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (farProc == IntPtr.Zero) throw (new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !")); if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length) throw (new Exception(" 参数个数及其传递方式的个数不匹配 .")); // 下面是创建 MyAssemblyName 对象并设置其 Name 属性 AssemblyName MyAssemblyName = new AssemblyName(); MyAssemblyName.Name = "InvokeFun"; // 生成单模块配件 AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, AssemblyBuilderAccess.Run); ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll"); // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ” MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType); // 获取一个 ILGenerator ,用于发送所需的 IL ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i; for (i = 0; i < ObjArray_Parameter.Length; i++) {// 用循环将参数依次压入堆栈 switch (ModePassArray_Parameter[i]) { case ModePass.ByValue: IL.Emit(OpCodes.Ldarg, i); break; case ModePass.ByRef: IL.Emit(OpCodes.Ldarga, i); break; default: throw (new Exception(" 第 " + (i + 1).ToString() + " 个参数没有给定正确的传递方式 .")); } } if (IntPtr.Size == 4) {// 判断处理器类型 IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32()); } else if (IntPtr.Size == 8) { IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64()); } else { throw new PlatformNotSupportedException(); } IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType); IL.Emit(OpCodes.Ret); // 返回值 MyModuleBuilder.CreateGlobalFunctions(); // 取得方法信息 MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun"); return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值 } // Invoke方法的第二个版本,它是调用了第一个版本的: /// <summary> /// 调用所设定的函数 /// </summary> /// <param name="IntPtr_Function"> 函数指针 </param> /// <param name="ObjArray_Parameter"> 实参 </param> /// <param name="TypeArray_ParameterType"> 实参类型 </param> /// <param name="ModePassArray_Parameter"> 实参传送方式 </param> /// <param name="Type_Return"> 返回类型 </param> /// <returns> 返回所调用函数的 object</returns> public static object Invoke(IntPtr IntPtr_Function, object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 2 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (IntPtr_Function == IntPtr.Zero) throw (new Exception(" 函数指针 IntPtr_Function 为空 !")); farProc = IntPtr_Function; return Invoke(ObjArray_Parameter, TypeArray_ParameterType, ModePassArray_Parameter, Type_Return); } } }
相关调用示例:
1. 加载 dll 和 调用方法
dllPath = Application.StartupPath + @"\test.dll"; LoadDllTool.LoadDll(dllPath); LoadDllTool.LoadFun("OpenForm"); object[] param = new object[7]; param[0] = handle; param[1] = loginUserID; param[2] = loginUserName; param[3] = accID; param[4] = year; param[5] = menuName; param[6] = path; Type[] paramType = new Type[7]; paramType[0] = typeof(IntPtr); paramType[1] = typeof(string); paramType[2] = typeof(string); paramType[3] = typeof(string); paramType[4] = typeof(string); paramType[5] = typeof(string); paramType[6] = typeof(string); LoadDllTool.ModePass[] paramModePass = new LoadDllTool.ModePass[7]; paramModePass[0] = LoadDllTool.ModePass.ByValue; paramModePass[1] = LoadDllTool.ModePass.ByValue; paramModePass[2] = LoadDllTool.ModePass.ByValue; paramModePass[3] = LoadDllTool.ModePass.ByValue; paramModePass[4] = LoadDllTool.ModePass.ByValue; paramModePass[5] = LoadDllTool.ModePass.ByValue; paramModePass[6] = LoadDllTool.ModePass.ByValue; this.DelphiHandle = (IntPtr)LoadDllTool.Invoke(param, paramType, paramModePass, typeof(IntPtr));
2. 释放Dll 和 调用函数示例
/// <summary> /// 释放 加载的 Dll /// </summary> private void unLoadDll() { try { LoadDllTool.LoadFun("CloseForm"); IntPtr rtnResult = (IntPtr)LoadDllTool.Invoke(new object[] { "hello" }, new Type[] { typeof(string) }, new LoadDllTool.ModePass[] { LoadDllTool.ModePass.ByValue }, typeof(IntPtr)); LoadDllTool.UnLoadDll(); } catch (Exception ex) { MessageBox.Show("释放Dll错误:" + ex.Message.ToString()); } }
如果在释放的时候出现异常的话,需要在 VS里做如下设置:
我是 VS2010
首先:Ctrl + Alt + E 调用 异常窗口 ,展开: Managed Debugging Assistants 然后, 再找到: LoaderLock 这个节点,把勾去掉,确定 就可以了.