2010-10-27 4 views
3

J'ai un tableau irrégulier que je dois passer à la méthode externe.Instruction fixe avec tableau dentelé

[DllImport(...)] 
private static extern int NativeMethod(IntPtr[] ptrArray); 

... 

fixed (ulong* ptr = array[0]) 
{ 
    for (int i = 0; i < array.Length; i++) 
    { 
     fixed (ulong* p = &array[i][0]) 
     { 
      ptrArray[i] = new IntPtr(p); 
     } 
    } 

    NativeMethod(ptrArray); 
} 

Le problème est que ptr est inutilisé et est retiré en raison de la compilation. La déclaration fixe selon elle est supprimée aussi. Donc, le tableau est déplacé par GC de cette façon que les éléments ptrArray deviennent invalides. Quel est le meilleur moyen de faire passer des tableaux dentelés sous la forme de tableaux unidimensionnels de pointeurs vers des méthodes natives?

Mise à jour:

Voici le code C++ pour NativeMethod:

NativeClass::NativeMethod(const int* array) 
+0

Quelle est votre méthode native ressemble à quel est le paramètre réel qu'il prend? –

+0

mis à jour - ajout du code C++ – levanovd

+0

La déclaration C++ n'est pas * * pour un tableau en escalier, qui nécessiterait un argument int **. Difficile de vous aider. –

Répondre

3

Votre problème est le fait que vous avez besoin tableau à être fixé puisque c'est celui que vous utilisez. Vous pouvez épingler le tableau afin que GC ne recueille pas:

GCHandle h = GCHandle.Alloc(array, GCHandleType.Pinned); 

MISE À JOUR

Comme vous l'avez souligné à juste titre, chaque tableau dans le tableau besoins épinglant aussi bien.

+0

Mais il peut toujours le déplacer. – levanovd

+0

Vous avez raison, je cherchais épinglé mais ne pouvais pas le trouver. – Aliostad

+0

Maintenant vous avez juste besoin de passer le h.AddrOfPinnedObject() à la méthode native. –

0

J'ai été en mesure de passer une matrice dentelée C# à C++ via une méthode Pinvoke externe sans utiliser de code C# dangereux comme dans l'exemple de code ci-dessous. Mais j'ai toujours mes préoccupations concernant le GC en mode non-débogage provoquant des effets secondaires indésirables. Voici le morceau de code de test (qui fonctionne en mode débogage):

[Test, Ignore] 
public void Test_JaggedArrayPInvoke() 
{ 
    var jaggedArray = new int[3][]; 
    jaggedArray[0] = new int[1] { 9 }; 
    jaggedArray[1] = new int[4] { 1, 2, 3, 8 }; 
    jaggedArray[2] = new int[2] { 1, 2 }; 

    //GCHandle mainHandle = GCHandle.Alloc(jaggedArray, GCHandleType.Pinned); //This does not work 

    var pinnedHandles = new GCHandle[3];      
    var jaggedArrayPtrs = new IntPtr[3]; 
    for (int i = 0; i < 3; i++) 
    { 
     pinnedHandles[i] = GCHandle.Alloc(jaggedArray[i], GCHandleType.Pinned); 
     jaggedArrayPtrs[i] = pinnedHandles[i].AddrOfPinnedObject(); 
    } 

    var result = JaggedArrayPInvoke_TEST(jaggedArrayPtrs); 

    Console.WriteLine(result); //returns 8 as it should. 

    //mainHandle.Free(); 
    for (int i = 0; i < 3; i++) 
    { 
     pinnedHandles[i].Free(); 
    } 
} 

//The C++ test method: 

extern "C" __declspec(dllexport) int __stdcall JaggedArrayPInvoke_TEST(int** jaggedArray); 
__declspec(dllexport) int __stdcall JaggedArrayPInvoke_TEST(int** jaggedArray) 
{ 
    return jaggedArray[1][3]; 
} 

Si je devais décommenter la partie mainHandle, je reçois une exception d'argument "objet contient des données non-primitive ou non blittable". Alors est-il possible d'épingler le jaggedArray et est-ce vraiment nécessaire? (Je me souviens vaguement que GC en mode release peut se souvenir de la mémoire déjà dans les méthodes s'il n'est plus utilisé.) Je pense que transformer le jaggedArray en une variable de champ de classe le rendrait sûr du point de vue du GC.

Questions connexes