C# 中动态调用C++动态链接
2015-08-26 11:23
771 查看
之前的文章中讲述过从DLL中导出变量,其中包括了静态加载和动态加载。
C#调用C++动态链接库同样分为静态加载和动态加载。
fellen的博客“WPF中使用MFC动态链接库(dll)函数”中讲述的是如何C#静态加载DLL,即需要将C++代码编译生成的Dll放在C#程序的Bin目录下,并在引入函数的位置加入[DllImport(“xxx.dll”)] 。
由于Dll路径的限制,使用的不是很方便,C#中我们经常通过配置动态的调用托管Dll,例如常用的一些设计模式:Abstract Factory, Provider, Strategy模式等等,那么是不是也可以这样动态调用C++动态链接呢?只要您还记得在C++中,通过LoadLibrary, GetProcess, FreeLibrary这几个函数是可以动态调用动态链接的(它们包含在kernel32.dll中),那么问题迎刃而解了。
首先看一下LoadLibrary, GetProcAddress, FreeLibrary这三个函数:
HMODULE WINAPI LoadLibrary ( LPCTSTR lpFileName);
说明:
载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源
返回值
lpLibFileName String,指定要载入的动态链接库的名称。采用与CreateProcess函数的lpCommandLine参数指定的同样的搜索顺序
一旦不需要,用FreeLibrary函数释放DLL
FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);
说明:
GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址。
hModule
[in] 包含此函数的DLL模块的句柄。LoadLibrary、AfxLoadLibrary 或者GetModuleHandle函数可以返回此句柄。
lpProcName
[in] 包含函数名的以NULL结尾的字符串,或者指定函数的序数值。如果此参数是一个序数值,它必须在一个字的底字节,高字节必须为0。
返回值:
如果函数调用成功,返回值是DLL中的输出函数地址。
如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。
GetProcAddress函数被用来检索在DLL中的输出函数地址。
lpProcName指针指向的函数名,拼写和大小写必须和DLL源代码中的模块定义文件(.DEF)中输出段(EXPORTS)中指定的相同。Win32 API函数的输出名可能不同于你在代码中调用的这些函数名,这个不同被宏隐含在相关的SDK头文件中。如果想得到更多信息,请参考Win32函数原型(Win32 Function Prototypes)。
lpProcName参数能够识别DLL中的函数,通过指定一个与函数相联系的序数值(在.DEF中的EXPORTS段)。GetProcAddress函数验证那个指定的序数值是否在输出的序数1和最高序数值之间(在.DEF中)。函数用这个序数值作为索引从函数表中读函数地址,假如.DEF 文件不连续地定义函数的序数值,如从1到N(N是输出的函数序数值),错误将会发生,GetProcAddress将会返回一个错误的、非空的地址,虽然指定的序数没有对应的函数。
为了防止函数不存在,函数应该通过名字指定而不是序数值。
BOOL WINAPI FreeLibrary ( In HMODULE hModule);
说明:
释放指定的动态链接库,它们早先是用LoadLibrary API函数装载的。
使用分两步走,首先是引入以上三个函数,并进行封装。新建一个类名为CSharpMethod.cs.需要注意的是必须引入命名空间using System.Runtime.InteropServices。
接下来第二步就是具体的使用了:
使用NativeMethod类动态读取C++Dll,获得函数指针,并且将指针封装成C#中的委托。原因很简单,C#中已经不能使用指针了,如下
int hModule = CSharpMethod.LoadLibrary(@“c:”CppDemo.dll”);
IntPtr intPtr = CSharpMethod.GetProcAddress(hModule, “Add”);
上诉代码中使用了Marshal.GetDelegateForFunctionPointer 方法 ,下面做简单介绍。
功能:将非托管函数指针转换为委托。
命名空间: System.Runtime.InteropServices
程序集: mscorlib(在 mscorlib.dll 中)
语法:public static Delegate GetDelegateForFunctionPointer( IntPtr ptr, Type t)
参数:ptr为要转换的非托管函数指针;t为要返回的委托的类型。
返回值:类型为System.Delegate 委托实例,可强制转换为适当的委托类型。
C#调用C++动态链接库同样分为静态加载和动态加载。
fellen的博客“WPF中使用MFC动态链接库(dll)函数”中讲述的是如何C#静态加载DLL,即需要将C++代码编译生成的Dll放在C#程序的Bin目录下,并在引入函数的位置加入[DllImport(“xxx.dll”)] 。
由于Dll路径的限制,使用的不是很方便,C#中我们经常通过配置动态的调用托管Dll,例如常用的一些设计模式:Abstract Factory, Provider, Strategy模式等等,那么是不是也可以这样动态调用C++动态链接呢?只要您还记得在C++中,通过LoadLibrary, GetProcess, FreeLibrary这几个函数是可以动态调用动态链接的(它们包含在kernel32.dll中),那么问题迎刃而解了。
首先看一下LoadLibrary, GetProcAddress, FreeLibrary这三个函数:
HMODULE WINAPI LoadLibrary ( LPCTSTR lpFileName);
说明:
载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源
返回值
lpLibFileName String,指定要载入的动态链接库的名称。采用与CreateProcess函数的lpCommandLine参数指定的同样的搜索顺序
一旦不需要,用FreeLibrary函数释放DLL
FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);
说明:
GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址。
hModule
[in] 包含此函数的DLL模块的句柄。LoadLibrary、AfxLoadLibrary 或者GetModuleHandle函数可以返回此句柄。
lpProcName
[in] 包含函数名的以NULL结尾的字符串,或者指定函数的序数值。如果此参数是一个序数值,它必须在一个字的底字节,高字节必须为0。
返回值:
如果函数调用成功,返回值是DLL中的输出函数地址。
如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。
GetProcAddress函数被用来检索在DLL中的输出函数地址。
lpProcName指针指向的函数名,拼写和大小写必须和DLL源代码中的模块定义文件(.DEF)中输出段(EXPORTS)中指定的相同。Win32 API函数的输出名可能不同于你在代码中调用的这些函数名,这个不同被宏隐含在相关的SDK头文件中。如果想得到更多信息,请参考Win32函数原型(Win32 Function Prototypes)。
lpProcName参数能够识别DLL中的函数,通过指定一个与函数相联系的序数值(在.DEF中的EXPORTS段)。GetProcAddress函数验证那个指定的序数值是否在输出的序数1和最高序数值之间(在.DEF中)。函数用这个序数值作为索引从函数表中读函数地址,假如.DEF 文件不连续地定义函数的序数值,如从1到N(N是输出的函数序数值),错误将会发生,GetProcAddress将会返回一个错误的、非空的地址,虽然指定的序数没有对应的函数。
为了防止函数不存在,函数应该通过名字指定而不是序数值。
BOOL WINAPI FreeLibrary ( In HMODULE hModule);
说明:
释放指定的动态链接库,它们早先是用LoadLibrary API函数装载的。
使用分两步走,首先是引入以上三个函数,并进行封装。新建一个类名为CSharpMethod.cs.需要注意的是必须引入命名空间using System.Runtime.InteropServices。
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace InteropDemo { public static class CSharpMethod { [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")] public static extern int LoadLibrary( [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName); [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")] public static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName); [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")] public static extern bool FreeLibrary(int hModule); } }
接下来第二步就是具体的使用了:
使用NativeMethod类动态读取C++Dll,获得函数指针,并且将指针封装成C#中的委托。原因很简单,C#中已经不能使用指针了,如下
int hModule = CSharpMethod.LoadLibrary(@“c:”CppDemo.dll”);
IntPtr intPtr = CSharpMethod.GetProcAddress(hModule, “Add”);
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace InteropDemo { class Program { static void Main(string[] args) { //1. 动态加载C++ Dll int hModule = CSharpMethod.LoadLibrary(@"c:\CppDemo.dll"); if (hModule == 0) return; //2. 读取函数指针 IntPtr intPtr = CSharpMethod.GetProcAddress(hModule, "Add"); //3. 将函数指针封装成委托 Add addFunction = (Add)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(Add)); //4. 测试 Console.WriteLine(addFunction(1, 2)); Console.Read(); } delegate int Add(int a, int b); } }
上诉代码中使用了Marshal.GetDelegateForFunctionPointer 方法 ,下面做简单介绍。
功能:将非托管函数指针转换为委托。
命名空间: System.Runtime.InteropServices
程序集: mscorlib(在 mscorlib.dll 中)
语法:public static Delegate GetDelegateForFunctionPointer( IntPtr ptr, Type t)
参数:ptr为要转换的非托管函数指针;t为要返回的委托的类型。
返回值:类型为System.Delegate 委托实例,可强制转换为适当的委托类型。
相关文章推荐
- C# 中动态调用C++动态链接
- 黑马程序员——C语言中的数据
- C++ 预处理、编译、汇编、链接
- 对比C语言中memccpy()函数和memcpy()函数的用法
- 《算法导论》的桶排序C++实现
- C++ STL 中的 bitset 用法
- [DP]HDOJ1158 Employment Planning
- C语言判断大小端对齐
- C语言strtok()函数:字符串分割
- C++注释规范
- C++虚继承的内存模型
- VC++编写DLL供C#使用
- 链表排序问题
- C++指针加整数、两个指针相减的问题
- NOI系列赛中 C++容器使用总结
- C语言判断字符是否为可打印字符的方法
- 【POJ2031】【最小生成树】【g++ f c++ lf】
- C++函数匹配
- POJ C++程序设计 编程题#1 编程作业—继承与派生
- C语言 异步回调