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

本文关键字:列表 PInvoke 返回 使用 | 更新日期: 2023-09-27 18:18:41

我需要返回一个点的列表,我从一个C dll到使用PInvoke的c#应用程序。这些是三维空间中的点[x,y,z]。点的数量因模型的类型而异。在C语言中,我用结构体的链表来处理这个问题。但是我不知道如何把它传递到c#。

在我看来,我必须返回一个灵活的二维数组,可能在一个结构体中。

有什么建议可以做到这一点吗?关于如何在C中返回它以及如何在c#中访问它的想法都是非常赞赏的。

使用PInvoke从C返回点(x,y,z)列表到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, e.g. 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封送处理程序所期望的。否则,您将在应用程序中获得内存泄漏和/或其他错误。

下面是我在验证我的答案时编译的简单示例的一个片段:

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