2014-09-20 94 views
1

我想在C#項目中使用C DLL。PInvoke具有不平衡的堆棧

我在C函數:

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

的sPiece結構:

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

我的PInvoke在C#:

[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我得到以下錯誤:

A call to PInvoke function 'Chess!Chess.Main::InitBoard' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

我嘗試調用約定更改爲Cdecl,但是當我運行它時,VS卡住。

我該怎麼辦?

+0

@Mark。我看到了這個答案,並沒有幫助,因爲我試圖改變PInvoke就像在那裏說的,它卡住了我的VS. – 2014-09-20 18:01:55

+1

InitBoard需要一個指針,但是你給它一個指針指針,因爲具有引用類型的ref是雙重間接。這是你會發現的下一個錯誤。 – usr 2014-09-20 21:11:51

回答

3

你有兩個問題。首先,你使用錯誤的調用約定。非託管函數使用cdecl,並且您需要託管函數來匹配該函數。

另一個問題,更具挑戰性的是二維數組。您可以使用p/invoke編組二維數組。你需要切換到一個一個維數組:

void InitBoard(sPiece board[]); 

的管理方是這樣的:

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

實施內部,你可以訪問內容是這樣的:

要從轉換行/ col對與線性索引使用以下關係:

index = row*8 + col; 

請注意,我也刪除了SetLastError的設置,因爲我非常懷疑你的功能叫做SetLastError

+0

你有我的upvote。我乍一看錯過了。在C++中,結構數組將全部位於給定基地的連續內存中,但在C#中,對象數組是引用數組,對象存儲在堆中。 – 2014-09-20 21:57:43

+0

謝謝你的詳細解答!這對我幫助很大! 我遇到了另一個問題,但我會寫在另一個帖子! – 2014-09-21 04:05:27

-3

在此討論看看:

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

Maybe the problem lies in the calling convention.

...

Apparently adding __std in the function prototype and declaration fixes it. Thanks

+0

我不明白在c原型上添加_std的位置? – 2014-09-20 17:57:21

+0

這應該是一個評論,而不是一個答案。 – Mark 2014-09-20 18:02:56

1

默認呼籲C約定/ C++是CDECL(見本article)。

__cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code. The following list shows the implementation of this calling convention.

在您的代碼中指定CallingConvention = CallingConvention.StdCall。這是不相容的。更改爲CallingConvention.Cdecl有關調用約定的更多信息,請參見here

+0

我嘗試改變它,而且當我調用PIvoke時沒有提供任何執行,但是卡住了vs – 2014-09-20 18:00:53

+0

可能是在函數InitBoard中存在一個會破壞堆或堆棧的錯誤(例如超出邊界數組邊界等) – 2014-09-20 18:03:02

+3

測試的一種方法是你的'InitBoard'函數什麼都不做,只能返回。它是否掛起或產生問題? – 2014-09-20 18:03:42