Как маршалировать неуправляемый буфер упакованных структур в c#

StackOverflow https://stackoverflow.com/questions/506231

Вопрос

Я (успешно) вызываю функцию Windows FilterSendMessage на c #, используя следующую подпись pinvoke:

[DllImport("fltlib.dll")]
    public static extern IntPtr FilterSendMessage(
        IntPtr hPort,
        IntPtr inBuffer,
        UInt32 inBufferSize,
        IntPtr outBuffer,
        UInt32 outBufferSize,
        out UInt32 bytesReturned);

В Буфер выхода параметр заполняется произвольным количеством структур (упакованных одна за другой), определенных в C как:

typedef struct _BAH_RECORD {

    int evt
    int len;
    WCHAR name[1];

} BAH_RECORD, *PBAH_RECORD;

В Имя полю присваивается строка юникода переменной длины, заканчивающаяся нулем.В лен поле описывает общий размер структуры в байтах (включая строку имени).Я уверен, что нет ничего плохого в том, как структуры обрабатываются в неуправляемой части вещей.

Моя проблема возникает, когда я пытаюсь перенаправить outBuffer к экземпляру структуры BAH_RECORD, определенной в c # как:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct BAH_RECORD
{
    public UInt32 evt;
    public UInt32 len;
    public string name;
}

IntPtr outBuffer = Marshal.AllocHGlobal(OUT_BUFFER_SIZE);

hResult = Win32.FilterSendMessage(hPortHandle, inBuffer, IN_BUFFER_SIZE, outBuffer, OUT_BUFFER_SIZE, out bytesReturned);

BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));

<snip>

Если я попытаюсь напечатать / просмотреть / отобразить bah.name, я получу мусор...

Чтобы подтвердить, что outBuffer действительно содержит достоверные данные, я проделал несколько грубых манипуляций с указателями в c #, чтобы пройти через него, вызвав Marshal.Дважды прочитайте INT32 (чтобы охватить первые 2 поля структуры), а затем Marshal.ReadByte несколько раз, чтобы заполнить byte[], который я затем использую в качестве аргумента для Encoding.Unicode.getString() ... строка выводится нормально, так что она определенно есть, я просто не могу заставить маршаллер обработать ее правильно (если он вообще может?)

Любая помощь приветствуется

Стив

Это было полезно?

Решение

Проблема в том, что строка 'name' в вашей структуре C # BAH_RECORD маршалируется как указатель на строку (WCHAR *), но на стороне C это встроенный буфер WCHAR.Таким образом, когда вы маршалируете свою структуру, среда выполнения считывает первые четыре байта буфера как указатель, а затем пытается прочитать строку, на которую он указывает.

К сожалению, среда выполнения не может автоматически маршалировать буферы переменного размера внутри структур, поэтому вам нужно будет использовать ручное маршалирование (или, как вы говорите, "взлом указателя").Но когда вы перемещаете указатель так, чтобы он указывал на буфер, вам не нужно читать байты по отдельности, а затем преобразовывать их в строку - просто вызовите Marshal.PtrToStringUni.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top