Returns a list of points (x, y, z) from C to C # using PInvoke

I need to return the list of points that I have from a C dll to a C # application using PInvoke. These are points in 3 dimensions [x, y, z]. The number of points depends on the model. In C, I am handling a linked list of structures. But I don’t see how I can pass this to C #.

As I see this, I need to return a flexible two-dimensional array, possibly to a structure.

Any suggestions on how to do this? Both ideas on how to get it back in C and how to access it in C # are very much appreciated.

+7
source share
1 answer

A linked list of structures can be passed back, but it would be rather difficult with it, since you would have to write code to scroll through pointers, read and copy data from the built-in memory to a managed memory space. Instead, I would recommend a simple array of structures.

If you have a C structure like the following (assuming 32 bit ints) ...

struct Point { int x; int y; int z; } 

... then you would imagine it almost the same as in C #:

 [StructLayout(LayoutKind.Sequential] struct Point { public int x; public int y; public int z; } 

Now, to pass the array back, it would be easier if your own code allocated the array and passed it as a pointer along with another pointer that defines the size in the elements.

Your C prototype might look like this:

 // Return value would represent an error code // (in case something goes wrong or the caller // passes some invalid pointer, eg a NULL). // Caller must pass in a valid pointer-to-pointer to // capture the array and a pointer to capture the size // in elements. int GetPoints(Point ** array, int * arraySizeInElements); 

The P / Invoke declaration will be as follows:

 [DllImport("YourLib.dll")] static extern int GetPoints( [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out Point[] array, out int arraySizeInElements); 

The MarshalAs attribute indicates that the array should be marshaled using the size specified in the second parameter (you can learn more about this in MSDN, "Default Marshaling for Arrays" ).

If you use this approach, please note that you should use CoTaskMemAlloc to allocate your own buffer, as this is what the .NET marshaler expects, otherwise you will get memory leaks and / or other errors in your application.

Here is a snippet from a simple example that I compiled while checking my answer:

 struct Point { int x; int y; int z; }; extern "C" int GetPoints(Point ** array, int * arraySizeInElements) { // Always return 3 items for this simple example. *arraySizeInElements = 3; // MUST use CoTaskMemAlloc to allocate (from ole32.dll) int bytesToAlloc = sizeof(Point) * (*arraySizeInElements); Point * a = static_cast<Point *>(CoTaskMemAlloc(bytesToAlloc)); *array = a; Point p1 = { 1, 2, 3 }; a[0] = p1; Point p2 = { 4, 5, 6 }; a[1] = p2; Point p3 = { 7, 8, 9 }; a[2] = p3; return 0; } 

A managed caller can process data very simply (in this example, I put all the interaction code in a static class called NativeMethods ):

 NativeMethods.Point[] points; int size; int result = NativeMethods.GetPoints(out points, out size); if (result == 0) { Console.WriteLine("{0} points returned.", size); foreach (NativeMethods.Point point in points) { Console.WriteLine("({0}, {1}, {2})", point.x, point.y, point.z); } } 
+5
source

All Articles