zoukankan      html  css  js  c++  java
  • C# 动态装载 DLL

    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 + @"	est.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 这个节点,把勾去掉,确定 就可以了.

  • 相关阅读:
    注册DLL的两种方法
    DVDRAM 格式化失败及视频文件分割软件
    提高ASP.NET效率的几个方面
    vs2003错误求救
    存储过程1
    制作VCD/DVD方法
    不能上网修复网络协议
    C语言运算符优先级
    黑马程序员 ObjectC 类 (一)
    进制转换的方法和技巧
  • 原文地址:https://www.cnblogs.com/wuyifu/p/3142197.html
Copyright © 2011-2022 走看看