MarshalAs вложенная структура - PullRequest
2 голосов
/ 12 октября 2010

У меня есть две структуры C ++, которые я должен отправлять в качестве параметров при вызове метода DLL из C #.

Например, давайте определим их как:

struct A
{
    int data;
}

struct B
{
    int MoreData;
    A * SomeData;
}

Метод, который янеобходимость вызова из C # имеет следующую подпись:

int operation (B * data);

(Обратите внимание, что у меня нет контроля ни над этими структурами C ++, ни над методами.)

В C # я определяю эти структуры какклассы:

[StructLayout(LayoutKind.Sequential)]
class A
{
    public int data;
}

[StructLayout(LayoutKind.Sequential)]
class B
{
    public int MoreData;

    [MarshalAs(UnmanagedType.Struct)]
    public A SomeData;
}

Я создал «отладочную dll» для вызова из C #, чтобы гарантировать, что все данные получены правильно в методах C ++.Пока правильно отправляются только те данные, которые находятся перед указателем вложенной структуры.

Когда я пытаюсь прочитать данные из вложенной структуры (B-> A-> data), я получаю ошибку нарушения чтения (AccessViolationException).

Как мне упорядочить вложенную структуру, чтобы ясможет прочитать его в методе C ++?

1 Ответ

5 голосов
/ 12 октября 2010

Ваша декларация на C # не эквивалентна, она генерирует встроенное поле SomeData. Другими словами, эквивалентная нативная декларация будет:

struct B
{
    int MoreData;
    A SomeData;
}

Маршаллер P / Invoke не может иметь дело с членом, указателем которого является. Это проблема управления памятью, очень мутно, кто владеет указателем и отвечает за его удаление. Чтобы сделать эту работу вообще, вы должны объявить структуру следующим образом:

    struct B {
        public int MoreData;
        public IntPtr SomeData;
    }

И собери это сам. Как это:

        var b = new B();
        b.MoreData = 0x12345678;
        var a = new A();
        a.Data = 0x789abcde;
        int len = Marshal.SizeOf(a);
        b.SomeData = Marshal.AllocCoTaskMem(len);
        try {
            Marshal.StructureToPtr(a, b.SomeData, false);
            someFunction(ref b);
        }
        finally {
            Marshal.FreeCoTaskMem(b.SomeData);
        }

В этом коде подразумевается, что указатель принадлежит управляемому коду и освобождает память (вызов FreeCoTaskMem). Если нативный код копирует переданную структуру, то это будет проблемой, он будет читать неверные данные или бомбу, когда попытается разыменовать указатель. Не освобождение памяти также не вариант, который приводит к необратимой утечке памяти. Поскольку нативный код не может использовать функцию free () для его освобождения. Если вы окажетесь в этом поле, то вам придется написать оболочку на языке C ++ / CLI, чтобы вы могли использовать функцию malloc () CRT.

...