Marshal C++ struct array en C#
interop marshalling (5)
Tengo la siguiente estructura en C ++:
#define MAXCHARS 15
typedef struct
{
char data[MAXCHARS];
int prob[MAXCHARS];
} LPRData;
Y una función en la que estoy p / invocando para obtener una matriz de 3 de estas estructuras:
void GetData(LPRData *data);
En C ++, simplemente haría algo como esto:
LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );
Y funcionaría bien, pero en C # parece que no puedo hacer que funcione. Creé una estructura C # como esta:
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;
/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}
Y si inicializo una matriz de 3 de ellos (y todas sus sub-matrices) y la paso a esto:
GetData(LPRData[] data);
Vuelve con éxito, pero los datos en la matriz LPRData no han cambiado.
Incluso he tratado de crear una matriz de bytes sin formato del tamaño de 3 LPRData y pasarlo a un prototipo de función como este:
GetData (byte [] datos);
Pero en ese caso obtendré la cadena de "datos" desde la primera estructura LPRData, pero nada después de ella, incluida la matriz "prob" del mismo LPRData.
¿Alguna idea de cómo manejar esto adecuadamente?
¿ OutAttribute parámetro GetData con OutAttribute ?
La combinación de InAttribute y OutAttribute es particularmente útil cuando se aplica a matrices y tipos formateados y no blittables. Las personas que llaman consideran que los cambios que un destinatario realiza en estos tipos solo cuando aplica ambos atributos.
El asistente de interoperabilidad de Invoke puede ayudar. http://clrinterop.codeplex.com/releases/view/14120
Intentaría agregar algunos atributos a tu estructura de decloración
[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 no pretende representar una variable
JaredPar también tiene razón al decir que usar la clase IntPtr podría ser útil, pero ha pasado bastante tiempo desde que utilicé PInvoke, así que estoy oxidado.
Se discutió un tema similar sobre esta cuestión , y una de las conclusiones fue que el parámetro CharSet
nombrado debe establecerse en CharSet.Ansi
. De lo contrario, estaríamos haciendo una matriz wchar_t
lugar de una matriz char
. Por lo tanto, el código correcto sería el siguiente:
[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;
}
Un truco cuando se trata de punteros es usar un IntPtr. Luego puede usar Marshal.PtrToStructure en el puntero e incrementar en función del tamaño de la estructura para obtener los resultados.
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;
}