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函数方法就介绍完了,希望对大家有所帮助。

新闻标题:C#调用DLL函数方法(下)
网站链接:http://www.shufengxianlan.com/qtweb/news2/422752.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联