web-dev-qa-db-fra.com

Marshal C ++ struct array in C #

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 p/invoque pour obtenir un tableau de 3 de ces structures:

void GetData(LPRData *data);

En C++, je ferais simplement 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 revient 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 bruts de la taille de 3 LPRData et de le transmettre à un prototype de fonction comme celui-ci:

GetData (octet [] données);

Mais dans ce cas, j'obtiendrai la chaîne "data" de la toute première structure LPRData, mais rien après, y compris le tableau "prob" du même LPRData.

Des idées sur la façon de gérer correctement cela?

28
Adam Haile

J'essaierais d'ajouter quelques 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 je n'ai pas utilisé PInvoke, je suis donc rouillé.

25
denny

Une astuce lorsque vous traitez avec des pointeurs est d'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;
}
13
JaredPar

L'assistant PInvoke Interop peut vous aider. http://clrinterop.codeplex.com/releases/view/1412

3
GregUzelac

Avez-vous marqué le paramètre GetData avec OutAttribute ?

La combinaison de InAttribute et OutAttribute est particulièrement utile lorsqu'elle est appliquée à des tableaux et à des types formatés non blittables. Les appelants ne voient les modifications apportées par un appelé à ces types que lorsque vous appliquez les deux attributs.

2
Constantin

Un sujet similaire a été discuté sur cette question , et l'une des conclusions était que le paramètre nommé CharSet doit être défini sur CharSet.Ansi. Sinon, nous ferions un wchar_t array au lieu d'un char array. 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;
}
2
Zenexer