最近经常在C#代码调用C++的dll文件,对于遇到的坑总结一下
1、针对C++的dll文件常见的几种约定方式
__stdcal:参数从右向左的顺序进行入栈,堆栈由被调用方进行释放,即C++的dll的函数进行堆栈自行管理
__cdecl:参数从右向左的顺序进行入栈,堆栈由调用方维护,即谁调用,是负责堆栈释放
C++的默认调用约定为__cdecl,我们在编写C#时,需要注意调用函数约定方式。
2、再C#代码中,默认的调用方式为__stdcall的约定的方式,如果无相关参数传递,则无论是否表明调用约定,均不会报错
extern "C" void __declspec(dllexport) TestWay(LPSTR lpPara)
{
cout<<"调用了TestWay 传递参数"<<lpPara<<endl;
return;
}
extern "C" void __declspec(dllexport) __stdcall TestWayStdcall(LPSTR lpPara)
{
cout<<"调用了TestWayStdcall传递参数"<<lpPara<<endl;
return;
}
internal class TestDllWay
{
[DllImport("TestDll.dll")]
public extern static void TestWay(string lpPara);
DllImport("TestDll.dll")]
public extern static void TestWayStdcall(string lpPara);
}
当你调用TestWay,程序会报错,当你加上CallingConvention = CallingConvention.Cdecl时,则可以调用成功!
3、CharSet.Ansi:以多字节字符串的形式封送字符串
CharSet.Unicode:以 Unicode 2 字节字符形式封送字符串
CharSet.Auto:
// 针对目标操作系统适当地自动封送字符串。在 Windows NT、Windows 2000、Windows XP 和 Windows Server
// 2003 系列上,默认值为 System.Runtime.InteropServices.CharSet.Unicode;在 Windows 98
// 和 Windows Me 上,默认值为 System.Runtime.InteropServices.CharSet.Ansi。尽管公共语言运行时默认值为
// System.Runtime.InteropServices.CharSet.Auto,但使用语言可重写此默认值。例如,默认情况下,C# 将所有方法和类型都标记为
// System.Runtime.InteropServices.CharSet.Ansi。
编写调用C++代码时,主要编码格式,否则会有意想不到的错误
以上C++ Dll代码再多字节编码编写
修改代码
[DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl,CharSet = CharSet.Auto)]
public extern static void TestWay(string lpPara);
再次调用是则会运行结果:
分析原因就是传递的编码格式导致这种C++char* 指针错误
4、关于传递int* 参数读取方式
extern "C" void __declspec(dllexport) TestIntVar(int *pIntValue)
{
*pIntValue = 10;
cout<<"调用了TestIntVar传递参数int = "<<*pIntValue<<endl;
}
[DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl)]
public extern static void TestIntVar(IntPtr ptr);
private void TestIntVal()
{
int ivalue = 0;
IntPtr ptr = Marshal.AllocHGlobal(4);
TestDllWay.TestIntVar(ptr);
ivalue = Marshal.ReadInt32(ptr);
Marshal.FreeHGlobal(ptr);
}
同样也可以使用out,ref的方式获取
[DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl)]
public extern static void TestIntVar(out int ptr);
[DllImport("TestDll.dll",CallingConvention = CallingConvention.Cdecl)]
public extern static void TestIntVar(ref int ptr);