C#动态调用c++DLL的方法

转载自:http://blog.csdn.net/amy0428/article/details/4236238

因为C#中使用DllImport是不能像动态load/unload assembly那样,所以只能借助API函数了。在kernel32.dll中,与动态库调用有关的函数包括[3]:

①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。

②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。

③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。

它们的原型分别是:

HMODULE LoadLibrary(LPCTSTR lpFileName);

FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);

BOOL FreeLibrary(HMODULE hModule);

现在,我们可以用IntPtr hModule=LoadLibrary(“Count.dll”);来获得Dll的句柄,用IntPtr farProc=GetProcAddress(hModule,”_count@4”);来获得函数的入口地址。

但是,知道函数的入口地址后,怎样调用这个函数呢?因为在C#中是没有函数指针的,没有像C++那样的函数指针调用方式来调用函数,所以我们得借助其它方法。经过研究,发现我们可以通过结合使用System.Reflection.Emit及System.Reflection.Assembly里的类和函数达到我们的目的。为了以后使用方便及实现代码的复用,我们可以编写一个类。

1)        dld类的编写:

1.       打开项目“Test”,打开类视图,右击“Tzb”,选择“添加”-->“类”,类名设置为“dld”,即dynamic loading dll 的每个单词的开头字母。

2.       添加所需的命名空间及声明参数传递方式枚举:

using System.Runtime.InteropServices; // 用DllImport 需用此命名空间

using System.Reflection; // 使用Assembly 类需用此命名空间

using System.Reflection.Emit; // 使用ILGenerator 需用此命名空间

 

3. 在namespace test中,“public class dld”的上面,添加如下代码声明参数传递方式枚举:

    /// <summary>

    /// 参数传递方式枚举,ByValue 表示值传递,ByRef 表示址传递

    /// </summary>

    public enum ModePass

    {

        ByValue = 0x0001,

        ByRef = 0x0002

}

4、在public class DLD中,添加如下代码:

    public class DLD

    {

        [DllImport("kernel32.dll")]

        public static extern IntPtr LoadLibrary(string lpFileName);

 

        [DllImport("kernel32.dll")]

        public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProceName);

 

        [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]

        public static extern bool FreeLibrary(IntPtr hModule);

 

        /// <summary>

 

        /// Loadlibrary 返回的函数库模块的句柄

 

        /// </summary>

 

        private IntPtr hModule = IntPtr.Zero;

 

        /// <summary>

 

        /// GetProcAddress 返回的函数指针

 

        /// </summary>

 

        private IntPtr farProc = IntPtr.Zero;

 

        /// <summary>

 

        /// 装载 Dll

 

        /// </summary>

 

        /// <param name="lpFileName">DLL 文件名 </param>

 

        public void LoadDll(string lpFileName)

        {

 

            hModule = LoadLibrary(lpFileName);

 

            if (hModule == IntPtr.Zero)

 

                throw (new Exception(没有找到 :" + lpFileName + "."));

        }

 

 

        /// <summary>

 

        /// 获得函数指针

 

        /// </summary>

 

        /// <param name="lpProcName"> 调用函数的名称 </param>

 

        public 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>

 

        /// 卸载 Dll

 

        /// </summary>

 

        public void UnLoadDll()

        {

 

            FreeLibrary(hModule);

 

            hModule = IntPtr.Zero;

 

            farProc = IntPtr.Zero;

 

        }

 

 

        /// <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 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);// 调用方法,并返回其值

 

        }

    }

2)        dld类的使用:

1. 打开项目“Test”,向“Form1”窗体中添加一个按钮,和一个TestBox,Name改为txRet。视图中双击按钮,在“button1_Click”方法体上面添加代码,创建一个dld类实例,并进行测试。具体如下:

private void button1_Click(object sender, EventArgs e)

        {

 

            int ret = 0;

            dld myDLD = new dld();

 

            myDLD.LoadDll("xxx.dll");

 

            myDLD.LoadFun("InitSDK");

 

            object[] Parameters = new object[] { }; // 实参为0

 

            Type[] ParameterTypes = new Type[] { }; // 实参类型为int

 

            ModePass[] themode = new ModePass[] { }; // 传送方式为值传

 

            Type Type_Return = typeof(int); // 返回类型为int

 

            ret = (int)myDLD.Invoke(Parameters, ParameterTypes, themode, Type_Return);

            txRet.Text = ret.ToString();

            if (ret != 1)

            {

                MessageBox.Show("InitSDK failed !");

            }

            if (ret == 1)

            {

                MessageBox.Show("InitSDK Sucessed !");

            }

        }

其中,xxx为要测试的dll名称,InitSDK为dll中的要测试的函数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值