Marshal C ++ struct array in C #
-
06-07-2019 - |
Domanda
Ho la seguente struttura in C ++:
#define MAXCHARS 15
typedef struct
{
char data[MAXCHARS];
int prob[MAXCHARS];
} LPRData;
E una funzione in cui sto p / invocando per ottenere una matrice di 3 di queste strutture:
void GetData(LPRData *data);
In C ++ farei semplicemente una cosa del genere:
LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );
E funzionerebbe bene, ma in C # non riesco a farlo funzionare. Ho creato una struttura C # in questo modo:
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;
/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}
E se inizializzo un array di 3 di questi (e tutti i loro sotto-array) e lo passo in questo:
GetData(LPRData[] data);
Ritorna con successo, ma i dati nell'array LPRData non sono cambiati.
Ho anche provato a creare un array di byte non elaborati della dimensione di 3 LPRData e passarlo in un prototipo di funzione come questo:
GetData (byte [] data);
Ma in quel caso otterrò i "dati" stringa dalla prima struttura LPRData, ma nulla dopo, inclusa la "prob" array dallo stesso LPRData.
Qualche idea su come gestirlo correttamente?
Soluzione
Vorrei provare ad aggiungere alcuni attributi al tuo decloration struct
[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;
}
* Nota TotalBytesInStruct non è destinato a rappresentare una variabile
JaredPar ha anche ragione nel dire che usare la classe IntPtr potrebbe essere utile, ma è da un po 'che non uso PInvoke, quindi sono arrugginito.
Altri suggerimenti
Un trucco quando si hanno a che fare con i puntatori è usare solo IntPtr. È quindi possibile utilizzare Marshal.PtrToStructure sul puntatore e incrementare in base alla dimensione della struttura per ottenere i risultati.
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 può essere d'aiuto. http://clrinterop.codeplex.com/releases/view/14120
Hai contrassegnato il parametro GetData con OutAttribute ?
Combinazione di InAttribute e OutAttribute è particolarmente utile quando applicato ad array e formattato, tipi non Blittable. I chiamanti vedono il le modifiche apportate da una chiamata a questi tipi solo quando applichi entrambi gli attributi.
Un argomento simile è stato discusso su questa domanda e quella di le conclusioni sono state che il parametro CharSet
deve essere impostato su CharSet.Ansi
. Altrimenti, creeremmo un array wchar_t
anziché un array char
. Pertanto, il codice corretto sarebbe il seguente:
[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;
}