2010-10-15 2 views
13

J'utilise une API écrite en C++ dans mon code (écriture en C#). L'API requiert un paramètre en tant que pointeur vers la structure. La structure se compose de s « Int » et tableaux Char: par exemplePassage d'une structure à l'API C++ en utilisant Marshal.StructureToPtr en C#

unsafe public struct ToBePassed 
    { 
     Int32 Num1; 
     Int32 Num2; 
     Char[] Data; // or fixed Char Data[255]; 
    } 

Je ne peux pas passer directement le pointeur de la structure à l'API, car dans ce cas, je reçois erreur « Pointeurs ne peut pas faire référence à des structures marshalés » . Le code est compilé avec succès mais cette erreur survient lorsque j'exécute (Debug) le code.

Maintenant, j'ai deux options: 1er: - Passant Structure par Réf: Je veux demander une API nécessitant un pointeur de structure peut recevoir l'adresse lorsque je passe la structure par réf. Notez que l'API retournera des données dans "Char [] Data".

2ème: - Utilisation de Marshal.StructureToPtr: Ceci convertira le pointeur de structure en IntPtr. Encore une fois le doute est le même, cette API va-t-elle le recevoir correctement?

Merci pour votre temps!

Cordialement, Swanand

Répondre

15

Si elle ne nécessite que pointeur, vous pouvez allouer de la mémoire non gérée, maréchal de la structure à la mémoire, et de transmettre ce pointeur à votre fonction. Ensuite, vous pouvez revenir à la structure (si vous le souhaitez) et libérer la mémoire. Avant de rassembler quoi que ce soit, vous devez définir correctement la structure. Quelque chose comme ceci:

[StructLayout(
    LayoutKind.Sequential,  //must specify a layout 
    CharSet = CharSet.Ansi)] //if you intend to use char 
public struct ToBePassed 
{ 
    public Int32 Num1; 
    public Int32 Num2; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] 
    public Char[] Data; //specify the size using MarshalAs 
} 

[DllImport("...")] 
public static extern void APICall(IntPtr argPtr); 


public static void CallFunction(ToBePassed managedObj) 
{ 
    IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(managedObj)); 

    Marshal.StructureToPtr(managedObj, unmanagedAddr, true); 

    APICall(unmanagedAddr); 

    Marshal.PtrToStructure(unmanagedAddr, managedObj); 

    Marshal.FreeHGlobal(unmanagedAddr); 
    unmanagedAddr = IntPtr.Zero; 
} 

[modifier]
pour simuler des tableaux de longueur variable, allouer de la mémoire non gérée dans la structure et initialize comme d'habitude.

[StructLayout(LayoutKind.Sequential)] 
public struct SomeStruct 
{ 
    public Int32 X; 
    public Int32 Y; 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct VLA 
{ 
    public Int32 intArrayLength; 
    public Int32 SomeStructArrayLength; 
    public IntPtr intArray; 
    public IntPtr SomeStructArray; 
} 

public static VLA CreateVLA(int[] intArray, SomeStruct[] SomeStructArray) 
{ 
    var vla = new VLA() 
    { 
     intArrayLength = intArray.Length, 
     SomeStructArrayLength = SomeStructArray.Length, 
     intArray = Marshal.AllocHGlobal(intArray.Length * Marshal.SizeOf(typeof(int))), 
     SomeStructArray = Marshal.AllocHGlobal(SomeStructArray.Length * Marshal.SizeOf(typeof(SomeStruct))), 
    }; 
    Marshal.Copy(intArray, 0, vla.intArray, intArray.Length); 
    //there's no overload to copy arbitrary arrays, do it manually 
    for (int i = 0; i < SomeStructArray.Length; i++) 
    { 
     Marshal.StructureToPtr(
      SomeStructArray[i], 
      vla.SomeStructArray + i * Marshal.SizeOf(typeof(SomeStruct)), 
      true); 
    } 
    return vla; 
} 
+0

Merci beaucoup très beaucoup! C'était génial!! Une question, l'API peut-elle renvoyer des données dans l'Array Char? – Swanand

+0

AFAIK, oui. L'API non gérée peut utiliser la mémoire pointée comme n'importe quelle autre mémoire, car elle n'est pas gérée. Dans ce cas, vous souhaiterez revenir à la structure pour récupérer le résultat dans le code managé. –

+0

Cela fonctionne bien .... Mais je suis confronté à un problème de plus, Maintenant, je veux transmettre des données à l'API (dire la version WriteToAPI de l'API mentionnée ci-dessus) Je vais transmettre des données à l'API maintenant. Mais comme cette donnée Char n'est pas de taille 255 (je veux passer des données de taille variable) elle donne une erreur car "Type ne peut être Marshaled car la longueur d'une instance Embedded Array ne correspond pas à la longueur déclarée dans Layout" – Swanand