我试图在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卡住了。
我应该做什么?
答案 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
解决它。感谢