【问题标题】:PInvoke has unbalanced the stackPInvoke 使堆栈不平衡
【发布时间】:2014-09-21 14:20:20
【问题描述】:

我正在尝试在 C# 项目中使用 C DLL。

我在 C 中有一个函数:

extern __declspec(dllexport) void InitBoard(sPiece board[8][8]);

片段结构:

typedef struct Piece
{
    ePieceType PieceType; //enum
    ePlayer Player; //enum
    int IsFirstMove;
} sPiece;

我在 C# 中有 PInvoke:

[DllImport("chess_api.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]ref sPiece[] board);

C# 上的 sPiece 结构:

[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
    public ePieceType PieceType;
    public ePlayer Player;
    public int IsFirstMove;
}

当我运行 PInvoke 时,我收到以下错误:

对 PInvoke 函数“Chess!Chess.Main::InitBoard”的调用使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

我尝试将调用约定更改为Cdecl,但是当我运行它时,VS 卡住了。

我该怎么办?

【问题讨论】:

  • @马克。我看到了这个答案,但没有帮助,因为我尝试像那里所说的那样更改 PInvoke,它卡住了我的 VS。
  • InitBoard 需要一个指针,但您给它一个指针指针,因为具有引用类型的 ref 是双重间接。这是您将发现的下一个错误。

标签: c# c struct pinvoke dllimport


【解决方案1】:

你有两个问题。首先,您使用了错误的调用约定。非托管函数使用 cdecl,您需要托管函数与之匹配。

另一个更具挑战性的问题是二维数组。

void InitBoard(sPiece board[8][8]);

您不能使用 p/invoke 编组二维数组。你需要切换到一维数组:

void InitBoard(sPiece board[]);

托管方如下所示:

[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard(sPiece[] board);

在实现内部,您可以像这样访问元素:

要将行/列对转换为线性索引,请使用以下关系:

index = row*8 + col;

请注意,我还删除了SetLastError 的设置,因为我非常怀疑您的函数确实调用了SetLastError

【讨论】:

  • 你有我的支持。乍一看,我错过了。在 C++ 中,结构数组将全部位于给定基址的连续内存中,但在 C# 中,对象数组是引用数组,并且对象存储在堆上。
  • 感谢您的详细解答!这对我帮助很大!我遇到了另一个问题,但我会在另一篇文章中写!
【解决方案2】:

C/C++ 的默认调用约定是 CDecl(参见 article)。

__cdecl 是 C 和 C++ 程序的默认调用约定。因为堆栈是由调用者清理的,所以它可以做可变参数函数。 __cdecl 调用约定比 __stdcall 创建更大的可执行文件,因为它要求每个函数调用都包含堆栈清理代码。以下列表显示了此调用约定的实现。

在您的代码中指定 CallingConvention = CallingConvention.StdCall 。这是不兼容的。更改为CallingConvention.Cdecl 更多关于调用约定的信息可以在here 找到。

【讨论】:

  • 我尝试更改它,当我调用 PIvoke 时 vs 卡住了,没有给出任何执行
  • 您的函数InitBoard 中是否有可能存在损坏堆或堆栈的错误(例如,运行超出数组边界等)
  • 一种测试方法是让您的InitBoard 函数只返回返回。它会挂起还是产生问题?
【解决方案3】:

看看这个讨论:

A call to PInvoke function '[...]' has unbalanced the stack

也许问题在于调用约定。

...

显然在函数原型和声明中添加__std 修复它。谢谢

【讨论】:

  • 我不明白在 c 原型上添加 _std 的位置?
  • 这应该是评论,而不是答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多