使用PInvoke返回从C到C#的点(x,y,z)列表

我需要使用PInvoke将我从C dll中获得的点列表返回给C#应用程序。 这些是3维[x,y,z]中的点。 点数因型号而异。 在C i中处理这个结构的链接列表。 但我不知道如何将其传递给C#。

我看到它的方式,我必须返回一个灵活的二维数组,可能在一个结构中。

有关如何做到这一点的任何建议? 关于如何在C中返回它以及如何在C#中访问它的想法都受到高度赞赏。

可以传回结构的链接列表,但处理起来会非常麻烦,因为您必须编写代码来遍历指针,读取数据并将数据从本机内存复制到托管内存空间。 我会推荐一个简单的结构数组。

如果您有一个如下所示的C结构(假设32位整数)…

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

…那么你在C#中几乎以同样的方式代表它:

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

现在要传回一个数组,最简单的方法是让你的本机代码分配数组并将其作为指针传回,同时另一个指针指定元素的大小。

您的C原型可能如下所示:

 // 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); 

P / Invoke声明将是这样的:

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

MarshalAs属性指定应使用第二个参数中指定的大小对数组进行封送处理(您可以在MSDN上阅读有关此内容的更多信息, “数组的默认封送处理” )。

如果您使用此方法,请注意您必须使用CoTaskMemAlloc来分配本机缓冲区,因为这是.NET编组程序所期望的。 否则,您将在应用程序中出现内存泄漏和/或其他错误。

这是我在validation我的答案时编译的简单示例的片段:

 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(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; } 

然后,托管调用者可以非常简单地处理数据(在此示例中,我将所有互操作代码放在名为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); } }