Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ref struct or class with P/Invoke

I know this subject was discussed many times here, but I couldn't find the answer for my specific situation.

I need to call in C# an unmanaged C method which takes a pointer on a struct object (I don't speak C fluently :

int doStuff(MYGRID* grid, int x);

But the struct itself references an other struct object :

struct MYGRID {

    int hgap;
    int vgap;

    MYIMAGE* image;

}

struct MYIMAGE {

    int res;
    int width;
    int height;

}

And I also need to set directly the image pointer like this :

MYGRID* pGrid = new MYGRID;
MYIMAGE* pImage = new MYIMAGE;
pGrid->image = pImage;

So, my question is : in C# code, should I use a "struct" object and passing it by "ref" like the P/Invoke Interop Assistant suggests me ? Which means the following code :

MyGrid myGrid = new MyGrid();
MyImage myImage = new MyImage();

myGrid.image = Marshal.AllocHGlobal(Marshal.SizeOf(image)); // A IntPtr in my struct
myGrid.image = Marshal.StructureToPtr(image, myGrid.image, false);

doStuff(ref myGrid, 0);

Or could I use "class" instead of "struct" in order to have the very simple following code :

MyGrid myGrid = new MyGrid();
MyImage myImage = new MyImage();

myGrid.image = myImage;

doStuff(myGrid, 0);

In the first case I use an "IntPtr" in my struct MyGrid, and just a MyImage object in the second case.

like image 241
Eric David Avatar asked Jan 14 '13 09:01

Eric David


1 Answers

Don't confuse C# struct with C++ struct. They are not the same thing. A C# struct is used to declare a value type. When you aggregate a value type in another type you store it directly in the containing instance instead of storing a reference to an instance stored on the heap. A C++ struct is simply a class where all members by default are public.

In your case, because MYGRID contains a pointer to MYIMAGE, you should use class as you are doing in you second example. However, the ref on the myGrid parameter should be removed.

Below is some sample code I have tested. The C++ code:

#include "windows.h"

struct MYIMAGE {

  int res;
  int width;
  int height;

};

struct MYGRID {

  int hgap;
  int vgap;

  MYIMAGE* image;

};

extern "C" __declspec(dllexport) int doStuff(MYGRID* grid, int x) {
  return 0;
}

Declaring the C# classes and the external function:

[StructLayout(LayoutKind.Sequential)]
class MyGrid {

  public int hgap;
  public int vgap;

  public IntPtr image;

}

[StructLayout(LayoutKind.Sequential)]
class MyImage {

  public int res;
  public int width;
  public int height;

}

[DllImport("MyDll")]
static extern int doStuff(MyGrid grid, int x);

Calling the external function:

MyImage image = new MyImage();

MyGrid grid = new MyGrid();
grid.image = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyImage)));
Marshal.StructureToPtr(image, grid.image, false);

doStuff(grid, 0);

If you turn on unmanaged debugging in your C# project you can use the debugger to step into the C++ function and verify that the classes have been correctly marshaled.

like image 149
Martin Liversage Avatar answered Sep 22 '22 05:09

Martin Liversage