Вернуть список точек (x, y, z) из C в C #, используя PInvoke - PullRequest
7 голосов
/ 25 декабря 2011

Мне нужно вернуть список точек, которые у меня есть, из C dll в приложение C #, используя PInvoke.Это точки в 3 измерениях [x, y, z].Количество баллов зависит от того, какая это модель.В C я рассматриваю это связанный список структур.Но я не понимаю, как я могу передать это в C #.

Как я вижу, я должен вернуть гибкий двумерный массив, вероятно, в структуре.

Есть предложения, как это можно сделать?Обе идеи о том, как вернуть его в C и как получить к нему доступ в C #, высоко ценятся.

1 Ответ

5 голосов
/ 25 декабря 2011

Связанный список структур может быть возвращен обратно, но с этим будет довольно сложно разобраться, так как вам придется писать код для циклического перемещения по указателям, чтения и копирования данных из native память в пространство управляемой памяти. Вместо этого я бы порекомендовал простой массив структур.

Если у вас есть структура 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...