Маршалинг сложной конструкции от C# до C - PullRequest
0 голосов
/ 09 июля 2020

Я безуспешно читаю о маршалинге структур между C и C# в 64-битной среде.

Мне нужно передать структуру

typedef struct DescStructTa
{
    char*           pszNa;
    unsigned long   ulTId;      
    char*           pszT;
    unsigned short  usRId;
    unsigned long   ulOff;
    unsigned long   ulSi;
    char            szAcc[2];
    unsigned char   bySSize;
} DescStruct;

из приложения C# в C DLL, созданную не нами, вызвав метод

MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);

После долгого поиска я написал следующий код:

[StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct PlcSymbolDescStruct
    {
        [MarshalAs(UnmanagedType.LPStr)]
        string pszNa;
        UInt32 ulTId;
        [MarshalAs(UnmanagedType.LPStr)]
        string pszT;
        [MarshalAs(UnmanagedType.U2)]
        ushort usRId;
        UInt32 ulOff;
        UInt32 ulSi;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
        string szAcc;
        [MarshalAs(UnmanagedType.U1)]
        byte bySSize;
    }

    [DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
         out uint pulNumS);

К сожалению, это все еще давая мне кучу коррупции. Если я попытаюсь упаковать меньше 8, это даст (как и ожидалось) нарушение прав доступа, но я ожидал, что это сработает, и я не могу понять, что может быть не так.

Есть предложения?

Заранее спасибо!

1 Ответ

0 голосов
/ 09 июля 2020

Я не уверен, что вам нужен Pack = 8. Упаковка по умолчанию более или менее совместима между C# и C ++.

Поле szAcc, вероятно, использует Unicode. Для переключения укажите CharSet=CharSet.Ansi в своем [StructLayout]

Наконец, если ваш C API имеет двойной указатель на их массив, вы должны выделить эту память особым образом, либо CoTaskMemAllo c или LocalAllo c, забыл какой. Вместо этого проще и эффективнее реализовать 2 функции. Один для получения необходимого размера массива. Еще один, чтобы записать их в буфер, выделенный вызывающей стороной, одиночный указатель в C ++, а не двойной указатель, и UnmanagedType.LPArray в C#.

Обновление

распределение не должно быть необходимым, поскольку в документации указано, что вызов возвращает указатель на его внутреннюю структуру данных

Среда выполнения не знает, что было написано в этой документации, все равно пытается освободить, и вылетает. Вы можете использовать out IntPtr в C# API и маршалировать вручную.

Если у вас современный. NET, используйте unsafe, приведите IntPtr к PlcSymbolDescStruct* необработанному указателю и создайте ReadOnlySpan<PlcSymbolDescStruct> из этот необработанный указатель.

, если вы застряли на старом рабочем столе. NET, вызовите Marshal.PtrToStructure<PlcSymbolDescStruct> в al oop, увеличивая указатель на Marshal.SizeOf<PlcSymbolDescStruct>() байтов между l oop итерациями. В этом случае нет необходимости использовать небезопасный код.

...