Using ref struct or class with P / Invoke

I know that this question has been discussed many times here, but I could not find the answer for my specific situation.

I need to call an unmanaged C method in C # that takes a pointer to a struct object (I don't say C smoothly:

int doStuff(MYGRID* grid, int x); 

But the structure itself refers to another struct object:

 struct MYGRID { int hgap; int vgap; MYIMAGE* image; } struct MYIMAGE { int res; int width; int height; } 

And I also need to set the direct image pointer as follows:

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

So my question is: in the C # code should I use the โ€œstructโ€ object and pass it โ€œrefโ€, as the P / Invoke Interop Assistant tells me? This 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 I could use a "class" instead of a "struct" to have a very simple following code:

 MyGrid myGrid = new MyGrid(); MyImage myImage = new MyImage(); myGrid.image = myImage; doStuff(myGrid, 0); 

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

+7
source share
3 answers

Do not confuse the C # structure with the C ++ construct. This is not the same thing. C # struct is used to declare a value type. When you combine a value type into another type, you store it directly in the containing instance instead of storing a reference to the instance stored on the heap. A C ++ structure is just a class in which all members are by default public.

In your case, since MYGRID contains a pointer to MYIMAGE , you should use class , as you do in your second example. However, the ref parameter in the MYGRID parameter must be removed.

Below is an example of the code that I tested. 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 C # classes and an 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); 

External function call:

 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 enable unmanaged debugging in a C # project, you can use the debugger to enter the C ++ function and make sure that the classes have been correctly marshaled.

+6
source

Below is my test code, you can consider it a link.

C # code - struct

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct Items { public double item1; public double item2; } 

C # code - caller class

 class ItemManager { [DllImport("CppLibrary.dll")] private static extern double Sum_(ref Items items); public static double Sum(Items items) { return Sum_(ref items); } } 

C ++ cpp

 double ItemManager::Sum(Items items) { return items.item1 + items.item2; } 

C ++ header

 class Items { public: double item1; double item2; }; class ItemManager { public: static double Sum(Items items); }; extern "C" __declspec(dllexport) double Sum_(Items * items) { return ItemManager::Sum( * items); } 
0
source

No, if MyImage was a class, then MyGrid.image will be a managed link that is different from the pointer.

-one
source

All Articles