C# 调用C++非托管dll

调用C++dll方法

我们在写C#程序时,常常需要调用C++编写的dll,在C#中调用dll是非常方便的,只需要把你要使用的dll放在程序的当前目录下,然后:

[DLLImport("DLL文件名")]
修饰符 extern static 返回变量类型 方法名称(参数列表)
// DLL文件名:你想要调用的库文件名
// 修饰符:public
// 返回变量类型:在dll文件中你需调用方法的返回变量类型
// 方法名称:在dll文件中你需调用方法的名称
// 参数列表:在dll文件中你需调用方法的列表

DLLImport()中还可以添加的属性有:
// EntryPoint 指示函数入口点 如:EntryPoint="MessageBoxA"
// CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
// SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;
// ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;
// PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;
// CallingConvention指示入口点的调用约定, 如:CallingConvention=CallingConvention.Winapi;

 


类型转换

由于C++的dll主要是基于win 32平台开发的非托管代码,或activeX的组件,而C#这样的托管代码是基于.net开发的。我们在C#中调用C++的dll时,会遇到参数的数据类型转换和指针或地址参数传送问题。这里把数据类型转换做一个总结:

C++到C#数据类型转换

C++中的数据类型

C#中的数据类型
char, INT8, CHARSByte
unsigned char, UINT8, UCHAR , BYTEByte
int *, int &ref int
char *, stringstring
short, short int, INT16, SHORTInt16
int, long, long int, INT32, LONG32, BOOL , INTInt32
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_tUInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINTUInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONGUInt64
float, FLOATSingle
double, long double, DOUBLEDouble
BSTR, LPCTSTRStringBuilder
LPCWSTR, handle, hwnd, unsigned char *, void *IntPtr

结构体的话,需要在C#里重新定义一个Struct

CallBack回调函数需要封装在一个委托里,如:delegate static extern int FunCallBack(string str);

 


使用举例

C++dll中函数:int __stdcall FunctionName(unsigned char *param1, unsigned int param2);

在C#中我们可以这样调用:

[DllImport("dll名字.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public extern static int FunctionName(ref byte param1,  UInt32 param2);

这里unsigned char *我们使用ref byte转换,unsigned int我们使用UInt32转换,我们调用这个函数时,利用参数传递地址只需要传入数组首地址就可以了

// 先初始化一个数组和一个unsigned int变量
string param1Str = "Hello World";
byte[] param1 = Encoding.Unicode.GetBytes(param1Str);
UInt32 param2 = 100;

// 要将其首地址传送过去,只要将param1数组的第一个元素用ref修饰
int a = FunctionName(ref param1[0], param2);

// 如果我们想要用param1作为输出,可以在调用函数之后直接使用或是把byte转换为string使用
byte[] param1 = new byte[100];
int a = FunctionName(ref param1[0], param2);
string param = Encoding.Unicode.GetString(param1);

对于char*,void*这种我们也可以直接用InPtr转换,还是以上面这个函数为例子,使用InPtr我们可以这样声明和调用:

[DllImport("dll名字.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public extern static int FunctionName(IntPtr param1,  UInt32 param2);

string param1Str = "Hello World";
IntPtr param1 = Marshal.StringToHGlobalUni(param1Str);
UInt32 param2 = 100;

int a = FunctionName(param1, param2);

// 用param1作为输出,我们要先使用Marshal.AllocHGlobal分配给param1内存,记得使用完后释放,否则会造成内存泄漏
IntPtr param1 = IntPtr.Zero;
param1 = Marshal.AllocHGlobal(100);

int a = FunctionName(param1, param2);
string param = Marshal.PtrToStringUni(param1);

// 使用完后,释放为param1申请的内存
Marshal.FreeHGlobal(param1);

除了使用Marshal.AllocHGlobal分配内存,也可以使用GCHandle分配内存

byte[] temp = new byte[100];
GCHandle param1GC = GCHandle.Alloc(temp, GCHandleType.Pinned);
IntPtr param1 = param1GC.AddrOfPinnedObject();

// 和使用Marshal.AllocHGlobal分配内存一样调用
int a = FunctionName(param1, param2);
string param = Marshal.PtrToStringUni(param1);

// 释放分配的内存
param1GC.Free();

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值