我需要通过在C#中导入DLL来调用C的以下函数,但它会出现以下错误。
我已导入DLL并成功执行其他函数,而此函数抛出错误。
C方法:
long __stdcall VBVMR_Input_GetDeviceDescA(long zindex, long * nType, char * szDeviceName, char * szHardwareId);
C#代码:
[DllImport("VoicemeeterRemote.dll", EntryPoint = "VBVMR_Input_GetDeviceDescA", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern int VBVMR_Input_GetDeviceDescA(long zindex, ref long nType, [Out] byte[] szDeviceName, [Out] byte[] szHardwareId);
long nType = 0;
byte[] c = new byte[100];
byte[] b = new byte[100];
long i=0;
int rep = VBVMR_Input_GetDeviceDescA(i,ref nType, c, b);
它在执行VBVMR_Input_GetDeviceDescA
时引发异常:
对PInvoke函数
'Voicemeter!Voicemeter.Program::VBVMR_Input_GetDeviceDescA'
的调用使堆栈失衡。这很可能是因为托管PInvoke签名与非托管目标签名不匹配。检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。
如果导出的函数是使用导出它的DLL中的调用约定__stdcall
声明的,则需要使用CallingConvention.StdCall
而不是Cdecl
在C#代码中导入它。
这种调用约定不匹配表现出“不平衡堆栈”的错误的原因是因为在调用cdecl
函数之后,调用代码应该在函数调用之后调整堆栈指针寄存器以删除传递给函数的参数。堆栈。但是,由于你的函数实际上是一个stdcall
函数,它在它使用的ret imm16
指令中执行它,所以当调用代码“清理堆栈”时你的堆栈会下溢。
接下来的问题是C#中的long
和MSVC中的long
是不同的大小,所以即使使用stdcall
调用约定,被调用函数也会弹出与其认为参数列表的正确大小相对应的字节数,但是因为C#的long
与MSVC的long
大小不同,在DLL函数结束时ret imm16
指令后堆栈仍然不平衡。然而,C#的int
类型对应于long
。