PInvoke使堆栈不平衡

本文关键字:不平衡 堆栈 PInvoke | 更新日期: 2023-09-27 18:23:36

我正试图在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卡住了。

我该怎么办?

PInvoke使堆栈不平衡

您有两个问题。首先,您使用了错误的调用约定。非托管函数使用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++的默认调用约定是CDecl(请参阅本文)。

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

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

看看这个讨论:

调用PInvoke函数';[…]';已使堆栈不平衡

也许问题出在呼叫约定上。

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