• C#调用DLL函数方法(下)


    本文的内容接着C#调用DLL函数方法(上)。

    因为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.添加所需的命名空间及声明参数传递方式枚举:

    1. using System.Runtime.InteropServices; // 用DllImport 需用此命名空间  
    2.  
    3. using System.Reflection; // 使用Assembly 类需用此命名空间  
    4.  
    5. using System.Reflection.Emit; // 使用ILGenerator 需用此命名空间  

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

    1. /// < summary>   
    2.  
    3.     /// 参数传递方式枚举,ByValue 表示值传递,ByRef 表示址传递  
    4.  
    5.     /// < /summary>   
    6.  
    7.     public enum ModePass  
    8.  
    9.     {  
    10.  
    11.         ByValue = 0x0001,  
    12.  
    13.         ByRef = 0x0002  
    14.  
    15. }  
    16.  

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

    1. public class DLD  
    2.  
    3.     {  
    4.  
    5.         [DllImport("kernel32.dll")]  
    6.  
    7.         public static extern IntPtr LoadLibrary(string lpFileName);  
    8.  
    9.    
    10.  
    11.         [DllImport("kernel32.dll")]  
    12.  
    13.         public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProceName);  
    14.  
    15.    
    16.  
    17.         [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]  
    18.  
    19.         public static extern bool FreeLibrary(IntPtr hModule);  
    20.  
    21.    
    22.  
    23.         /// < summary>   
    24.  
    25.    
    26.  
    27.         /// Loadlibrary 返回的函数库模块的句柄   
    28.  
    29.    
    30.  
    31.         /// < /summary>   
    32.  
    33.    
    34.  
    35.         private IntPtr hModule = IntPtr.Zero;  
    36.  
    37.    
    38.  
    39.         /// < summary>   
    40.  
    41.    
    42.  
    43.         /// GetProcAddress 返回的函数指针   
    44.  
    45.    
    46.  
    47.         /// < /summary>   
    48.  
    49.    
    50.  
    51.         private IntPtr farProc = IntPtr.Zero;  
    52.  
    53.    
    54.  
    55.         /// < summary>   
    56.  
    57.    
    58.  
    59.         /// 装载 Dll   
    60.  
    61.    
    62.  
    63.         /// < /summary>   
    64.  
    65.    
    66.  
    67.         /// < param name="lpFileName">DLL 文件名 < /param>   
    68.  
    69.    
    70.  
    71.         public void LoadDll(string lpFileName)  
    72.  
    73.         {  
    74.  
    75.    
    76.  
    77.             hModule = LoadLibrary(lpFileName);  
    78.  
    79.    
    80.  
    81.             if (hModule == IntPtr.Zero)  
    82.  
    83.    
    84.  
    85.                 throw (new Exception(" 没有找到 :" + lpFileName + "."));  
    86.  
    87.         }  
    88.  
    89.    
    90.  
    91.    
    92.  
    93.         /// < summary>   
    94.  
    95.    
    96.  
    97.         /// 获得函数指针   
    98.  
    99.    
    100.  
    101.         /// < /summary>   
    102.  
    103.    
    104.  
    105.         /// < param name="lpProcName"> 调用函数的名称 < /param>   
    106.  
    107.    
    108.  
    109.         public void LoadFun(string lpProcName)  
    110.  
    111.         { // 若函数库模块的句柄为空,则抛出异常   
    112.  
    113.    
    114.  
    115.             if (hModule == IntPtr.Zero)  
    116.  
    117.    
    118.  
    119.                 throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));  
    120.  
    121.    
    122.  
    123.             // 取得函数指针   
    124.  
    125.    
    126.  
    127.             farProc = GetProcAddress(hModule, lpProcName);  
    128.  
    129.    
    130.  
    131.             // 若函数指针,则抛出异常   
    132.  
    133.    
    134.  
    135.             if (farProc == IntPtr.Zero)  
    136.  
    137.    
    138.  
    139.                 throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 "));  
    140.  
    141.    
    142.  
    143.         }  
    144.  
    145.    
    146.  
    147.         /// < summary>   
    148.  
    149.    
    150.  
    151.         /// 卸载 Dll   
    152.  
    153.    
    154.  
    155.         /// < /summary>   
    156.  
    157.    
    158.  
    159.         public void UnLoadDll()  
    160.  
    161.         {  
    162.  
    163.    
    164.  
    165.             FreeLibrary(hModule);  
    166.  
    167.    
    168.  
    169.             hModule = IntPtr.Zero;  
    170.  
    171.    
    172.  
    173.             farProc = IntPtr.Zero;  
    174.  
    175.    
    176.  
    177.         }  
    178.  
    179.    
    180.  
    181.    
    182.  
    183.         /// < summary>   
    184.  
    185.    
    186.  
    187.         /// 调用所设定的函数   
    188.  
    189.    
    190.  
    191.         /// < /summary>   
    192.  
    193.    
    194.  
    195.         /// < param name="ObjArray_Parameter"> 实参 < /param>   
    196.  
    197.    
    198.  
    199.         /// < param name="TypeArray_ParameterType"> 实参类型 < /param>   
    200.  
    201.    
    202.  
    203.         /// < param name="ModePassArray_Parameter"> 实参传送方式 < /param>   
    204.  
    205.    
    206.  
    207.         /// < param name="Type_Return"> 返回类型 < /param>   
    208.  
    209.    
    210.  
    211.         /// < returns> 返回所调用函数的 object< /returns>   
    212.  
    213.    
    214.  
    215.         public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return)  
    216.  
    217.         {  
    218.  
    219.    
    220.  
    221.             // 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常   
    222.  
    223.    
    224.  
    225.             if (hModule == IntPtr.Zero)  
    226.  
    227.    
    228.  
    229.                 throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));  
    230.  
    231.    
    232.  
    233.             if (farProc == IntPtr.Zero)  
    234.  
    235.    
    236.  
    237.                 throw (new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !"));  
    238.  
    239.    
    240.  
    241.             if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length)  
    242.  
    243.    
    244.  
    245.                 throw (new Exception(" 参数个数及其传递方式的个数不匹配 ."));  
    246.  
    247.    
    248.  
    249.             // 下面是创建 MyAssemblyName 对象并设置其 Name 属性   
    250.  
    251.    
    252.  
    253.             AssemblyName MyAssemblyName = new AssemblyName();  
    254.  
    255.    
    256.  
    257.             MyAssemblyName.Name = "InvokeFun";  
    258.  
    259.    
    260.  
    261.             // 生成单模块配件   
    262.  
    263.    
    264.  
    265.             AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, AssemblyBuilderAccess.Run);  
    266.  
    267.    
    268.  
    269.             ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll");  
    270.  
    271.    
    272.  
    273.             // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ”   
    274.  
    275.    
    276.  
    277.             MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType);  
    278.  
    279.    
    280.  
    281.             // 获取一个 ILGenerator ,用于发送所需的 IL   
    282.  
    283.    
    284.  
    285.             ILGenerator IL = MyMethodBuilder.GetILGenerator();  
    286.  
    287.    
    288.  
    289.             int i;  
    290.  
    291.    
    292.  
    293.             for (i = 0; i <  ObjArray_Parameter.Length; i++)  
    294.  
    295.             {// 用循环将参数依次压入堆栈   
    296.  
    297.    
    298.  
    299.                 switch (ModePassArray_Parameter[i])  
    300.  
    301.                 {  
    302.  
    303.    
    304.  
    305.                     case ModePass.ByValue:  
    306.  
    307.    
    308.  
    309.                         IL.Emit(OpCodes.Ldarg, i);  
    310.  
    311.    
    312.  
    313.                         break;  
    314.  
    315.    
    316.  
    317.                     case ModePass.ByRef:  
    318.  
    319.    
    320.  
    321.                         IL.Emit(OpCodes.Ldarga, i);  
    322.  
    323.    
    324.  
    325.                         break;  
    326.  
    327.    
    328.  
    329.                     default:  
    330.  
    331.    
    332.  
    333.                         throw (new Exception(" 第 " + (i + 1).ToString() + " 个参数没有给定正确的传递方式 ."));  
    334.  
    335.    
    336.  
    337.                 }  
    338.  
    339.    
    340.  
    341.             }  
    342.  
    343.    
    344.  
    345.             if (IntPtr.Size == 4)  
    346.  
    347.             {// 判断处理器类型   
    348.  
    349.    
    350.  
    351.                 IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32());  
    352.  
    353.    
    354.  
    355.             }  
    356.  
    357.    
    358.  
    359.             else if (IntPtr.Size == 8)  
    360.  
    361.             {  
    362.  
    363.    
    364.  
    365.                 IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64());  
    366.  
    367.    
    368.  
    369.             }  
    370.  
    371.    
    372.  
    373.             else 
    374.  
    375.             {  
    376.  
    377.    
    378.  
    379.                 throw new PlatformNotSupportedException();  
    380.  
    381.    
    382.  
    383.             }  
    384.  
    385.    
    386.  
    387.             IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType);  
    388.  
    389.    
    390.  
    391.             IL.Emit(OpCodes.Ret); // 返回值   
    392.  
    393.    
    394.  
    395.             MyModuleBuilder.CreateGlobalFunctions();  
    396.  
    397.    
    398.  
    399.             // 取得方法信息   
    400.  
    401.    
    402.  
    403.             MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun");  
    404.  
    405.    
    406.  
    407.             return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值   
    408.  
    409.    
    410.  
    411.         }  
    412.  
    413.     }  
    414.  

    2) dld类的使用:

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

    1. private void button1_Click(object sender, EventArgs e)  
    2.  
    3.         {  
    4.  
    5.    
    6.  
    7.             int ret = 0;  
    8.  
    9.             dld myDLD = new dld();  
    10.  
    11.    
    12.  
    13.             myDLD.LoadDll("xxx.dll");  
    14.  
    15.    
    16.  
    17.             myDLD.LoadFun("InitSDK");  
    18.  
    19.    
    20.  
    21.             object[] Parameters = new object[] { }; // 实参为0   
    22.  
    23.    
    24.  
    25.             Type[] ParameterTypes = new Type[] { }; // 实参类型为int   
    26.  
    27.    
    28.  
    29.             ModePass[] themode = new ModePass[] { }; // 传送方式为值传  
    30.  
    31.    
    32.  
    33.             Type Type_Return = typeof(int); // 返回类型为int  
    34.  
    35.    
    36.  
    37.             ret = (int)myDLD.Invoke(Parameters, ParameterTypes, themode, Type_Return);  
    38.  
    39.             txRet.Text = ret.ToString();  
    40.  
    41.             if (ret != 1)  
    42.  
    43.             {  
    44.  
    45.                 MessageBox.Show("InitSDK failed !");  
    46.  
    47.             }  
    48.  
    49.             if (ret == 1)  
    50.  
    51.             {  
    52.  
    53.                 MessageBox.Show("InitSDK Sucessed !");  
    54.  
    55.             }  
    56.  
    57.         }  
    58.  

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

    至此,C#调用DLL函数方法就介绍完了,希望对大家有所帮助。

  • 相关阅读:
    python文件打开方式详解——a、a+、r+、w+、rb、rt区别
    io.UnsupportedOperation: can't do nonzero cur-relative seeks”错误
    端口三种模式:access,hybrid,trunk
    水仙花数
    maktrans和translate详解
    实战NFS服务搭建与配置
    except 配合 shell实现公钥分发脚本
    linux系统免秘钥分发文件
    rsync + inotify 实现远程实时同步数据
    通过rsync实现全网数据备份检查脚本
  • 原文地址:https://www.cnblogs.com/batys/p/2451190.html
Copyright © 2020-2023  润新知