C#动态调用C++编写的DLL函数

C#动态调用C++编写的DLL函数

动态加载DLL需要使用Windows API函数:LoadLibrary、GetProcAddress以及FreeLibrary。我们可以使用DllImport在C#中使用这三个函数。

[DllImport("Kernel32")] 
public static extern int GetProcAddress(int handle, String funcname);

[DllImport("Kernel32")] 
public static extern int LoadLibrary(String funcname);

[DllImport("Kernel32")] 
public static extern int FreeLibrary(int handle);

当我们在C++中动态调用Dll中的函数时,我们一般的方法是: 
假设DLL中有一个导出函数,函数原型如下: 
BOOL __stdcall foo(Object &object, LPVOID lpReserved);

1、首先定义相应的函数指针: 
typedef BOOL (__stdcall *PFOO)(Object &object, LPVOID lpReserved);

2、调用LoadLibrary加载dll: 
HINSTANCE hInst = ::LoadLibraryW(dllFileName);

3、调用GetProcAddress函数获取要调用函数的地址: 
PFOO foo = (PFOO)GetProcAddress(hInst,"foo"); 
if(foo == NULL) 

FreeLibrary(hInst); 
return false; 
}

4、调用foo函数: 
BOOL bRet = foo(object,(LPVOID)NULL);

5、使用完后应释放DLL: 
FreeLibrary(hInst);

那么在C#中应该怎么做呢?方法基本上一样,我们使用委托来代替C++的函数指针,通过.NET Framework 2.0新增的函数GetDelegateForFunctionPointer来得到一个委托的实例:

下面封装了一个类,通过该类我们就可以在C#中动态调用Dll中的函数了:

public class DLLWrapper 

///<summary> 
/// API LoadLibrary 
///</summary> 
[DllImport("Kernel32")] 
public static extern int LoadLibrary(String funcname);

///<summary> 
/// API GetProcAddress 
///</summary> 
[DllImport("Kernel32")] 
public static extern int GetProcAddress(int handle, String funcname);

///<summary> 
/// API FreeLibrary 
///</summary> 
[DllImport("Kernel32")] 
public static extern int FreeLibrary(int handle);

///<summary> 
///通过非托管函数名转换为对应的委托, by jingzhongrong 
///</summary> 
///<param name="dllModule">通过LoadLibrary获得的DLL句柄</param> 
///<param name="functionName">非托管函数名</param> 
///<param name="t">对应的委托类型</param> 
///<returns>委托实例,可强制转换为适当的委托类型</returns> 
public static Delegate GetFunctionAddress(int dllModule, string functionName, Type t) 

int address = GetProcAddress(dllModule, functionName); 
if (address == 0) 
return null; 
else 
return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t); 
}

///<summary> 
///将表示函数地址的IntPtr实例转换成对应的委托, by jingzhongrong 
///</summary> 
public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t) 

if (address == IntPtr.Zero) 
return null; 
else 
return Marshal.GetDelegateForFunctionPointer(address, t); 
}

///<summary> 
///将表示函数地址的int转换成对应的委托,by jingzhongrong 
///</summary> 
public static Delegate GetDelegateFromIntPtr(int address, Type t) 

if (address == 0) 
return null; 
else 
return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t); 

}

通过这个类,我们这样调用DLL:

1、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。

2、加载DLL: 
int hModule = DLLWrapper.LoadLibrary(dllFilePath); 
if (hModule == 0) 
return false;

3、获取相应的委托实例: 
FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO)); 
if (foo == null) 

DLLWrapper.FreeLibrary(hModule); 
return false; 
}

4、调用函数: 
foo(...);

5、.NET并不能自动释放动态加载的DLL,因此我们在使用完DLL后应该自己释放DLL: 
DLLWrapper.FreeLibrary(hModule);

下面我们将就委托应如何声明进行相应的讨论,在实际操作过程中,我发现使用DllImport方法和动态调用方法两者在C#中对DLL中函数原型的声明是有些区别的,下面我介绍动态调用中委托的声明:

1、首先应该注意的是,C++中的类型和C#中类型的对应关系,比如C++中的long应该对应C#中的Int32而不是long,否则将导致调用结果出错。

2、结构的声明使用StructLayout对结构的相应布局进行设置,具体的请查看MSDN:

使用LayoutKind指定结构中成员的布局顺序,一般可以使用Sequential: 
[StructLayout(LayoutKind.Sequential)] 
struct StructVersionInfo 

public int MajorVersion; 
public int MinorVersion; 

另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在C#中声明为class: 
[StructLayout(LayoutKind.Sequential)] 
class StructVersionInfo 

public int MajorVersion; 
public int MinorVersion; 
}

对应C++中的声明: 
typedef struct _VERSION_INFO 

int MajorVersion; 
int MinorVersion; 
} VERSION_INFO, *PVERSION_INFO;

如果结构中使用到了字符串,最好应指定相应的字符集: 
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]

部分常用的声明对应关系(在结构中): 
C++:字符串数组 
wchar_t Comments[120]; 
C#: 
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)] 
public string Comments;

C++:结构成员 
VERSION_INFO ver; 
C# 
publicStructVersionInfo ver;

C++:函数指针声明 
PFOO pFoo; //具体声明见文章前面部分 
C#: 
publicIntPtr pFoo; //也可以为 public int pFoo; 
//不同的声明方法可以使用上面DLLWrapper类的相应函数获取对应的委托实例

如果在结构中使用到了union,那么可以使用FieldOffset指定具体位置。

3、委托的声明:

当C++编写的DLL函数需要通过指针传出将一个结构:如以下声明: 
void getVersionInfo(VERSION_INFO *ver); 
对于在C#中声明为class的结构(当VERSION_INFO声明为class) 
delegate voidgetVersionInfo(VERSION_INFO ver); 
如果结构声明为struct,那么应该使用如下声明: 
delegate voidgetVersionInfo(refVERSION_INFO ver); 
注意:应该使用ref关键字。

如果DLL函数需要传入一个字符串,比如这样: 
BOOL __stdcall jingzhongrong1(const wchar_t* lpFileName, int* FileNum); 
那么使用委托来调用函数的时候应该在C#中如下声明委托: 
delegate bool jingzhongrong1( 
[MarshalAs(UnmanagedType.LPWStr)]String FileName, 
ref int FileNum); 
注意:应该使用[MarshalAs(UnmanagedType.LPWStr)]和String进行声明。

如果要在DLL函数中传出一个字符串,比如这样: 
void __stdcall jingzhongrong2( 
wchar_t* lpFileName, //要传出的字符串 
int* Length); 
那么我们如下声明委托: 
//使用委托从非托管函数的参数中传出的字符串, 
//应该这样声明,并在调用前为StringBuilder预备足够的空间 
delegate void jingzhongrong2( 
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpFileName, 
ref int Length, 
); 
在使用函数前,应先为StringBuilder声明足够的空间用于存放字符串: 
StringBuilder fileName = new StringBuilder(FileNameLength);

参考: http://www.2cto.com/kf/201007/52562.html

时间: 2024-10-13 22:27:47

C#动态调用C++编写的DLL函数的相关文章

c# 动态调用.net编写的webservices接口

c# 动态调用.net编写的webservices接口 创建类WebServiceHelper: public class WebServiceHelper { #region 动态调用WebService动态调用地址 /// < summary> /// 动态调用web服务 /// < /summary> /// < param name="url">WSDL服务地址< /param> /// < param name="

使用clr 调用C#编写的dll中的方法的全解释

使用clr 调用C#编写的dll中的方法的全解释1.数据库初始化:将下面这段代码直接在运行就可以初始化数据库了exec sp_configure 'show advanced options', '1';goreconfigure;goexec sp_configure 'clr enabled', '1'goreconfigure;exec sp_configure 'show advanced options', '1'; go ALTER DATABASE DB_Name set TRUS

vb做界面调用c编写的dll

没有真正的做过C++项目,如何在短时间内完成模型软件的方法,成为前段时间需要考虑的问题,通过vbs脚本到vb到gis一直到如今的建模软件,我想到用比较容易上手的吧vb来做界面,(网上有的一些前辈也是这么应用采纳的,极大的肯定了我的方向),核心计算部分用的是c编写的dll,计算引擎直接利用epanet,数据库上打算先放置一边,留着后续升级的时候进行采用,因为定位的是一种辅助调度分析的工具,因此想着先运行起来. vb环境:VB6.0(企业版) C开发环境:DEV C++ 数据库:SQL2008 vb

Delphi7调用DelphiXE编写的DLL问题

http://bbs.csdn.net/topics/380045353 用DelphiXE在WIN2008下编写一个访问WebServices的DLL ws.dll,只有一个输出函数,如下: function Login(URL:PAnsiChar; UserName: PAnsiChar; UserPass: PAnsiChar; LocalLogin: Boolean):PAnsiChar;var   tStr:String;begin try            Result := P

C#调用C++编写的dll

界面还是C#写的方便点,主要是有一个可视化的编辑器,不想画太多的时间在界面上.但是自己又对C++了解的多一些,所以在需要一个良好的界面的情况下,使用C++来写代码逻辑,将其编译成一个dll,然后用C#写界面, 1.C++编写的dll代码如下: extern "C" __declspec(dllexport) int testAdd(int a,int b) { return a+b; } //一个简单测试字符类型的例子 extern "C" __declspec(d

PB调用C#编写的Dll类库

在c# 中编写com组件,供PB调用实例 前言:c#中写的dll直接是不能被pb调用的,只有写成com组件才可以调用,所以用c#写dll时要注意. c#中新建类库 类库类型为通用类库,项目名为AddCom 1.配置:右键点击解决方案资源管理器中的AddCom,选择“属性”,选择“应用程序”->“程序集信息”,勾选“使程序集COM可见”,然后点击“生成”,选择“为COM互操作注册” 2.打开AssemblyInfo.cs文件,设置[assembly: ComVisible(true)],如果不改则

C++项目中采用CLR的方式调用C#编写的dll

1.注意事项:在编写C#DLL类库时,最好不要出现相同的命名空间,否则在C++中调用可能会出现编译错误.2.将C#的源码生成的"dll"文件复制到C++项目中的Debug目录下3.将C++项目属性设置为公共语言运行时支持4.在项目中导入dll文件和引用民命空间 #using "../debug/xxx.dll" using namespace xxxx; 5.实例化C#对象:CL ^cl = gcnew CL(); 6.调用C#带out string[] 参数的方法

PB调用C#编写的DLL

C#以其简单易用,功能强大深受大家喜爱.PowerBuilder作为C/S的MIS开发工具,十分简单灵活,开发时间短,开发及维护成本低,一直是中小企业信息管理系统的首选开发工具.但是PB的局限性限制了它进一步的发展,这个就不多说了,玩PB的朋友都清楚.PB如何调用C#写的DLL,这个兴趣一上来,就忍不住要解决它.经过多方查找资料加上自己写代码测试,算是解决这个难题.下面列出开发步骤及各种设置选项(开发工具VS2008SP1+PB9.0-8836) 首先我们打开VS2008,新建一个项目,如图 接

Java中调用Delphi编写的DLL

有些时候,要写一些程序,在 JAVA 里面好难实现, 但如果使用其它编程语言却又比较容易时,我们不妨通过 JNI 来让不同语言的程序共同完成. JNI 的教程, 网上 C 的比较多,Java 也提供了 javah.exe 为 C 语言的 JNI 程序生成头文件, 如果你是一个 Delphi 编程员, 能否让 JAVA 与 Delphi 程序交互呢? 答案是肯定的,今天我们就来看一下一个简单的例子. Helloworld. 主要是来认识一下, JAVA 怎样调用 Delphi 程序的方法. 好的,