Маршал C ++ массив структур в C #
-
06-07-2019 - |
Вопрос
У меня есть следующая структура в 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 (byte [] data);
Но в этом случае я получу " данные " строка из самой первой структуры LPRData, но ничего после нее, включая " prob " массив из того же LPRData.
Есть идеи, как правильно с этим справиться?
Решение
Я бы попробовал добавить некоторые атрибуты в вашу декорацию структуры
[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, так что я ржавый.
Другие советы
Одна хитрость при работе с указателями - просто использовать 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;
}
PInvoke Interop Assistant может помочь. http://clrinterop.codeplex.com/releases/view/14120
Вы пометили параметр GetData с помощью OutAttribute а>?
Объединение атрибута InAttribute и OutAttribute особенно полезен применительно к массивам и отформатирован, неблизкие типы. Абоненты видят изменения вызываемого абонента к этим типам только когда вы применяете оба атрибута.
Аналогичная тема обсуждалась в этом вопросе , и на были сделаны выводы, что для именованного параметра 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;
}