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

use*_*281 7 c c# pinvoke

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

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

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

bob*_*mcr 5

结构的链表可以传回,但它是相当对付一个麻烦,因为你必须通过指针写代码回路,阅读和从本机内存为管理存储空间复制数据.我会推荐一个简单的结构数组.

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

struct Point
{
    int x;
    int y;
    int z;
}
Run Code Online (Sandbox Code Playgroud)

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

[StructLayout(LayoutKind.Sequential]
struct Point
{
    public int x;
    public int y;
    public int z;
}
Run Code Online (Sandbox Code Playgroud)

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

您的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);
Run Code Online (Sandbox Code Playgroud)

P/Invoke声明将是这样的:

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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