У меня есть следующая структура на С ++:
#define MAXCHARS 15
typedef struct
{
char data[MAXCHARS];
int prob[MAXCHARS];
} LPRData;
И функцию, которую я вызываю, чтобы получить массив из трех таких структур:
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.
Есть идеи, как правильно с этим справиться?
Один из приемов работы с указателями - просто использовать 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;
}
что я действительно понимаю, так это то, что вы назначаете приращение с помощью приращения pointer.toInt + sizeof (struct). Разве приращение не будет только sizeof (struct)?
Но в примере GetData просто отправляет intptr, где создается память и где она освобождается.
Я бы попробовал добавить некоторые атрибуты к деклорации вашей структуры
[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, прошло довольно много времени, так что я заржавел.
Я использовал этот подход, но получаю исключения в Mono, когда переменные устанавливаются в нулевые ссылки. Например, "prob" имеет значение null, поэтому он не хочет работать. Должен ли я когда-нибудь узнавать об этом, или это должно каким-то образом обрабатываться фреймворком? Спасибо
Помощник взаимодействия PInvoke может помочь. http://clrinterop.codeplex.com/releases/view/14120
Вы отметили параметр GetData с помощью OutAttribute?
Combining the InAttribute and OutAttribute is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes.
Аналогичная тема обсуждалась на этот вопрос, и один из выводов заключался в том, что именованный параметр 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;
}
должна быть строка 11 либо: измените
+=
на=
иToInt32
наToInt64
, если работает 64-разрядная версия; или удалитьvalue.ToInt32()
?