1

I'm trying to use a C DLL in C# project.

I have a function in C:

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

the sPiece struct :

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

I have PInvoke in C#:

[DllImport("chess_api.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]ref sPiece[] board);

the sPiece struct on C#:

[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
    public ePieceType PieceType;
    public ePlayer Player;
    public int IsFirstMove;
}

When I run the PInvoke I get the following error:

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.

I try to change the calling convention to Cdecl, but when I run it, the VS stuck.

What am I supposed to do?

Dmitry
  • 13,797
  • 6
  • 32
  • 48
Hodaya Shalom
  • 4,327
  • 12
  • 57
  • 111
  • @Mark. I saw this answer and it didn't help, cause I try to change the PInvoke like was said there, and it stuck my VS. – Hodaya Shalom Sep 20 '14 at 18:01
  • 1
    InitBoard expects a pointer but you are giving it a pointer-pointer because ref with a reference type is a double indirection. This is the next bug that you'll discover. – usr Sep 20 '14 at 21:11

3 Answers3

4

You've got two problems. Firstly, you use the wrong calling convention. The unmanaged function use cdecl, and you need your managed function to match that.

The other problem, much more challenging, is the two dimensional array.

void InitBoard(sPiece board[8][8]);

You cannot marshal a two dimensional array using p/invoke. You need to switch to a one dimensional array:

void InitBoard(sPiece board[]);

The managed side looks like this:

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

Inside the implementation, you can access elements like this:

To convert from a row/col pair to a linear index use the following relationship:

index = row*8 + col;

Note that I also removed the setting of SetLastError since I doubt very much that your function does call SetLastError.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • You have my upvote. I missed that on first glance. In C++ an array of structs will all be in contiguous memory from a given base but in C# object arrays are array of references, and the objects are stored on the heap. – Michael Petch Sep 20 '14 at 21:57
  • Thank you for the detailed answer! It helped me a lot! I ran into another problem, but that I will write in another post! – Hodaya Shalom Sep 21 '14 at 04:05
1

The default calling convention for C/C++ is CDecl (see this 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.

In your code you specify CallingConvention = CallingConvention.StdCall . This is incompatible. Change to CallingConvention.Cdecl More on calling conventions can be found here .

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • I try to change it, and the vs stuck when I call the PIvoke, without giving any execption – Hodaya Shalom Sep 20 '14 at 18:00
  • Is it possible that there is a bug in your function `InitBoard` that is corrupting the heap or stack (for example running beyond the edge of array boundaries etc) – Michael Petch Sep 20 '14 at 18:03
  • 3
    One way to test is have your `InitBoard` function do nothing but return. Does it hang or produce problems? – Michael Petch Sep 20 '14 at 18:03
-3

Take a look at this discussion:

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

Community
  • 1
  • 1
FoggyDay
  • 11,962
  • 4
  • 34
  • 48