Маршал C ++ массив структур в C # - PullRequest
28 голосов
/ 09 октября 2008

У меня есть следующая структура в C ++:

#define MAXCHARS 15

typedef struct 
{
    char data[MAXCHARS];
    int prob[MAXCHARS];
} LPRData;

И функция, которую я запускаю, чтобы получить массив из 3 таких структур:

void GetData(LPRData *data);

В C ++ я бы просто сделал что-то вроде этого:

LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );

И это будет работать нормально, но в C # я не могу заставить его работать. Я создал структуру C # примерно так:

public struct LPRData
{

    /// char[15]
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    /// int[15]
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}

И если я инициализирую массив из 3 из них (и всех их подмассивов) и передам его в это:

GetData(LPRData[] data);

Возвращается с успехом, но данные в массиве LPRData не изменились.

Я даже пытался создать необработанный байтовый массив размером 3 LPRData и передать его в прототип функции следующим образом:

GetData (данные байта []);

Но в этом случае я получу строку «data» из самой первой структуры LPRData, но ничего после нее, включая массив «prob» из того же LPRData.

Есть идеи, как правильно с этим справиться?

Ответы [ 5 ]

25 голосов
/ 09 октября 2008

Я бы попробовал добавить некоторые атрибуты в вашу структуру декорации

[StructLayout(LayoutKind.Sequential, Size=TotalBytesInStruct),Serializable]
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;

/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}

* Примечание. TotalBytesInStruct не предназначен для представления переменной

JaredPar также прав, что использование класса IntPtr может быть полезным, но прошло довольно много времени с тех пор, как я использовал PInvoke, поэтому я ржавый.

13 голосов
/ 09 октября 2008

Одна хитрость при работе с указателями - просто использовать IntPtr. Затем вы можете использовать Marshal.PtrToStructure для указателя и увеличивать его в зависимости от размера структуры, чтобы получить результаты.

static extern void GetData([Out] out IntPtr ptr);

LPRData[] GetData()
{
    IntPtr value;
    LPRData[] array = new LPRData[3];
    GetData(out value);
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = Marshal.PtrToStructure(value, typeof(LPRData));
        value += Marshal.SizeOf(typeof(LPRData));
    }
    return array;
}
3 голосов
/ 16 октября 2008

Может помочь PInvoke Interop Assistant. http://clrinterop.codeplex.com/releases/view/14120

2 голосов
/ 08 февраля 2012

Подобная тема обсуждалась на этом вопросе , и один из выводов заключался в том, что для именованного параметра CharSet должно быть установлено значение CharSet.Ansi. В противном случае мы создали бы массив wchar_t вместо массива char. Таким образом, правильный код будет выглядеть следующим образом:

[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct LPRData
{
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
    public string data;

    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
    public int[] prob;
}
2 голосов
/ 19 октября 2008

Вы пометили параметр GetData OutAttribute ?

Объединение атрибута InAttribute и OutAttribute особенно полезен применительно к массивам и отформатирован, неблизкие типы. Абоненты видят изменения вызываемого абонента к этим типам только когда вы применяете оба атрибута.

...