不平衡的堆栈!

7

我写了一个VC++动态链接库。其中一个方法的声明如下:

extern "C" _declspec(dllexport)
void startIt(int number)
{
     capture = cvCaptureFromCAM(number);
}

我在使用P/Invoke的C#代码中调用这个dll。我的声明如下:

[DllImport("Tracking.dll", EntryPoint = "startIt")]
        public extern static void startIt(int number);

我会把函数写在代码里,然后这样调用:

startIt(0);

现在,当遇到这行代码时,编译器会抛出以下错误:
A call to PInvoke function 'UsingTracking!UsingTracking.Form1::startIt' has 
unbalanced the stack. This is likely because the managed PInvoke signature does 
not match the unmanaged target signature. Check that the calling convention 
and parameters of the PInvoke signature match the target unmanaged signature.

我不明白为什么会出现这个错误,因为托管代码和非托管代码的签名是相同的。此外,在另一台机器上,相同的代码在 Visual Studio 中运行得很完美。所以,这让我认为抛出的错误是误导性的。
请帮忙解决。
谢谢。

你的机器中有一个是x86架构的,另一个是x64架构的吗? - Anton Tykhyy
不,它们两个都是x86架构。只是一个运行Win7,另一个运行XP。 - Jayesh
3个回答

14

当你使用p/invoke调用外部函数时,所使用的calling convention默认为__stdcall。因为你的函数使用__cdecl约定,所以你需要将其声明为这种约定:

[DllImport("Tracking.dll", EntryPoint = "startIt",
    CallingConvention = CallingConvention.Cdecl)]
public extern static void startIt(int number);

谢谢,那个方法有效!不过我还是想知道为什么在Win7上没有抛出同样的错误! - Jayesh
@James,除非Win7在幕后执行特殊的管道以切换到正确的调用约定,否则它应该有。 - Frédéric Hamidi
@James,你在Win7上使用了完全相同的dll还是重新编译了它? - Constantin
@Constatin 是的,我已经在XP上重新构建了它! - Jayesh
2
stdcallcdecl的区别在于,使用stdcall时,被调用函数负责从堆栈中移除参数(通常使用RET x将ESP移动x个字节),而使用cdecl时则由调用者负责。 - CodesInChaos
显示剩余3条评论

6
你的DllImport属性中是否缺少CallingConvention=CallingConvention.Cdecl

1
这是我在调试这个问题时遇到的问题(在切换到.NET 4后)。如果您没有指定,stdcall将是默认值! - Jonathon Reinhart
这也解决了我的问题,因为我将一个旧的.NET 2解决方案切换到.NET 4。 - Christophe Geers

4
Constantin和Frederic Hamidi已经正确回答了如何解决这个问题。这可以帮助避免最终的堆栈溢出。我自己也多次遇到这个问题。实际上,.NET 4已经在32位x86机器(不是64位)上启用了一个受控调试助手,用于调试(而不是发布)构建,检查是否有错误指定的p/invoke调用。这篇MSDN文章详细介绍了这一点:http://msdn.microsoft.com/en-us/library/0htdy0k3.aspx。Stephen Cleary应该得到在这篇文章中发现这个问题的功劳:pinvokestackimbalance -- how can I fix this or turn it off?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接