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卡住了。
我该怎么办?
您有两个问题。首先,您使用了错误的调用约定。非托管函数使用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
修复它。谢谢