Marshal C ++ struct tableau en C #
-
06-07-2019 - |
Question
J'ai la structure suivante en C ++:
#define MAXCHARS 15
typedef struct
{
char data[MAXCHARS];
int prob[MAXCHARS];
} LPRData;
Et une fonction dans laquelle je suis invoquée pour obtenir un tableau de trois de ces structures:
void GetData(LPRData *data);
En C ++, je voudrais simplement faire quelque chose comme ceci:
LPRData *Results;
Results = (LPRData *)malloc(MAXRESULTS*sizeof(LPRData));
GetData( Results );
Et cela fonctionnerait très bien, mais en C #, je n'arrive pas à le faire fonctionner. J'ai créé une structure C # comme ceci:
public struct LPRData
{
/// char[15]
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string data;
/// int[15]
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 15)]
public int[] prob;
}
Et si j'initialise un tableau de 3 d'entre eux (et tous leurs sous-tableaux) et le passe dans ceci:
GetData(LPRData[] data);
Il retourne avec succès, mais les données du tableau LPRData n'ont pas changé.
J'ai même essayé de créer un tableau d'octets brut de la taille de 3 LPRData et de le transférer dans un prototype de fonction comme celui-ci:
GetData (octet [] données);
Mais dans ce cas, j'obtiendrai les "données". chaîne de la toute première structure LPRData, mais rien après, y compris le "prob" tableau du même LPRData.
Avez-vous des idées sur la manière de gérer cela correctement?
La solution
J'essaierais d'ajouter des attributs à votre déclinaison de structure
[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;
}
* Remarque TotalBytesInStruct n'est pas destiné à représenter une variable
JaredPar a également raison de dire que l’utilisation de la classe IntPtr pourrait être utile, mais cela fait un certain temps que j’ai utilisé PInvoke, donc je suis rouillé.
Autres conseils
Une astuce lorsque vous utilisez des pointeurs consiste à utiliser simplement un IntPtr. Vous pouvez ensuite utiliser Marshal.PtrToStructure sur le pointeur et incrémenter en fonction de la taille de la structure pour obtenir vos résultats.
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;
}
L'assistant d'interopérabilité PInvoke peut vous aider. http://clrinterop.codeplex.com/releases/view/14120
Avez-vous marqué le paramètre GetData avec OutAttribute ?
Combinaison de InAttribute et OutAttribute est particulièrement utile lorsqu'il est appliqué à des tableaux et formaté, types non-blittable. Les appelants voient le modifie un appel fait à ces types uniquement lorsque vous appliquez les deux attributs.
Un sujet similaire a été abordé dans cette question et dans celle de les conclusions ont été que le paramètre nommé CharSet
doit être défini sur CharSet.Ansi
. Sinon, nous créerions un tableau wchar_t
au lieu d'un tableau char
. Ainsi, le code correct serait le suivant:
[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;
}