切换到VS2010后,托管调试助手在从C#应用程序调用非托管C ++函数时显示有关不平衡堆栈的错误。
通常的嫌疑人似乎没有引起这个问题。还有别的我应该检查一下吗? VS2008构建的C ++ DLL和C#应用程序从来没有出现问题,没有奇怪或神秘的错误 - 是的,我知道这并不意味着什么。
以下是检查的内容:
C#:
[DllImport("Correct.dll", EntryPoint = "SuperSpecialOpenFileFunc", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern short SuperSpecialOpenFileFunc(ref SuperSpecialStruct stuff);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct SuperSpecialStruct
{
public int field1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string field2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string field3;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string field4;
public ushort field5;
public ushort field6;
public ushort field7;
public short field8;
public short field9;
public uint field10;
public short field11;
};
C ++:
short SuperSpecialOpenFileFunc(SuperSpecialStruct * stuff);
struct SuperSpecialStruct
{
int field1;
char field2[256];
char field3[20];
char field4[10];
unsigned short field5;
unsigned short field6;
unsigned short field7;
short field8;
short field9;
unsigned int field10;
short field11;
};
这是错误:
托管调试助手 'PInvokeStackImbalance'检测到了 “托管应用程序路径”中的问题。
附加信息:致电 PInvoke功能 'SuperSpecialOpenFileFunc'有 堆栈不平衡。这很可能 因为托管PInvoke签名 与非托管目标不匹配 签名。检查是否正在通话 公约和参数 PInvoke签名与目标匹配 非托管签名。
答案 0 :(得分:59)
正如Dane Rose's comment中所述,您可以在C ++函数上使用__stdcall
,也可以在CallingConvention = CallingConvention.Cdecl
上声明DllImport
。
答案 1 :(得分:9)
你在C#中指定stdcall而不是在C ++中指定stdcall,这里的不匹配将导致函数和调用者从堆栈中弹出参数。
另一方面,有一个编译器开关会打开stdcall作为默认的调用约定,(-Gz)你使用它吗?
或者在C ++中试试这个
short __stdcall SuperSpecialOpenFileFunc(SuperSpecialStruct * stuff);
答案 2 :(得分:3)
在结构的C#声明中没有指定填充,但在C ++版本中没有。由于您混合的char数组不是四的倍数和奇数的2字节短路,编译器可能在结构中插入填充并添加结尾。
尝试在#pragma pack
中包装结构以确保没有填充。
#pragma pack(push)
#pragma pack(1)
// The struct
#pragma pack(pop)
答案 3 :(得分:2)
有与上述相同的问题 - 多年来一直运行良好的非托管C ++应用程序。当我们升级到VS2010时,我们开始收到PInvokeStackUnbalanced消息。
如上所述将“__stdcall”添加到C ++签名中会使问题消失。
答案 4 :(得分:1)
很好。我的更新功能定义如下:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
效果很好。