PInvoke已经使堆栈失衡

时间:2014-09-20 17:48:14

标签: c# c struct pinvoke dllimport

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

我在C中有一个函数:

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

sPiece结构:

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卡住了。

我应该做什么?

3 个答案:

答案 0 :(得分:4)

你有两个问题。首先,您使用错误的调用约定。非托管函数使用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

答案 1 :(得分:1)

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

  

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

在您的代码中指定CallingConvention = CallingConvention.StdCall。这是不兼容的。更改为CallingConvention.Cdecl有关调用约定的更多信息,请here

答案 2 :(得分:-3)

看看这个讨论:

  

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

     

问题可能在于调用约定。

     

...

     

显然在函数原型和声明中添加__std   解决它。感谢