2013-06-13 3 views
2

Je commence à avoir des problèmes avec ce problème. J'ai cherché et recherché de l'aide, et tout ce que j'ai essayé ne semble pas fonctionner. Je fais évidemment quelque chose de mal.Passer une structure contenant un tableau int de taille inconnue de C# à c et retour

Quoi qu'il en soit - i ont une structure C# définie comme:

public struct TestStruct 
[StructLayout(LayoutKind.Sequential)] 
{ 
    public int num; 
    public IntPtr intArrayPtr; 
} 

et dans le corps principal de mon code C# j'ai:

public class Testing 
{ 
    [DllImport("testing.dll")] 
    static extern void Dll_TestArray(out IntPtr intArrayPtr); 

    public GetArray() 
    { 
     IntPtr structPtr = IntPtr.Zero; 
     TestStruct testStruct; 
     structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(testStruct)); 
     Marshal.StructureToPtr(testStruct, structPtr, false); 

     Dll_TestArray(structPtr); 

     testStruct = (TestStruct) Marshal.PtrToStructure(structPtr, typeof(TestStruct)); 
    } 
} 

Alors maintenant, pour le C++ partie. A partir de la structure:

struct TestStruct 
{ 
    public: 
    int num; 
    int* intArray; 
} 

et maintenant les fonctions:

extern "C" __declspec(dllexport) void Dll_TestArray(TestStruct *&testStruct) 
{ 
    int num = 15; 
    testStruct->num = num; 
    testStruct->intArray = new int[num]; 

    for (int i = 0; i < num; i++) 
    testStruct->intArray[i] = i+1; 
} 

Alors - Le problème que je vais avoir, est que lorsque je reçois la structure arrière en C#, ma structure est pas comment devrait être. Je peux voir que le champ num a été rempli correctement: il montre 15. Cependant, c'est le IntPtr qui est toujours mis à zéro. La création de tableau effectuée en C++ n'a pas été réalisée en C#.

Si j'essaie de revenir en arrière, et revenir dans la fonction dll, je peux voir que le tableau a été créé ok et conserve toujours l'information. Ainsi, le C# intptr dans la structure n'est pas défini sur le pointeur créé en C++ (si cela a du sens).

Donc, ma question est vraiment - comment fait-on ce travail correctement? Je veux pouvoir revenir à partir de la DLL, une structure qui contient toutes les informations dont j'ai besoin. C'est-à-dire, dans cet exemple, le nombre d'éléments et le pointeur vers le tableau. De cette façon, je peux alors faire un Marshal.Copy sur l'intptr, pour obtenir le tableau.

S'il y a une autre façon de le faire, je suis plus qu'heureux de le faire. J'ai essayé plusieurs façons déjà, en vain. Cela inclut d'essayer la structure suivante en C# (qui contient le tableau int, plutôt que IntPtr):

public struct TestStruct 
{ 
    public int num; 

    // i have tried various methods to marshal this- eg: 
    // [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_I4] 
    public int[] intArray; 
} 

Et j'ai aussi essayé passer la structure par référence elle-même, plutôt que d'un IntPtr. Toute aide à ce sujet serait massivement appréciée. Je suis incapable de changer le code C++, mais le code C# peut être changé.

+0

Est-ce votre vrai code? Qu'est-ce que 'structPtr. false' et 'TestStruct * & testStruct' ?? De plus, si 'intArrayPtr' est défini comme' out', pourquoi remplissez-vous la structure avant d'appeler 'Dll_TestArray'? Veuillez poster un code réel (couper et coller est une bonne idée). –

+0

C'est du vrai code, oui. Ce n'est pas particulièrement utile, j'essaie juste d'obtenir quelque chose de très basique, donc je peux travailler sur quelque chose de plus avancé. J'ai édité quelques-unes des fautes de frappe. Je n'utilisais pas le paramètre out avant, mais j'ai commencé à le faire quand j'ai lu quelque part que ce serait une bonne chose. Donc, si nous utilisons le paramètre out, c'est pourquoi j'utilise le TestStruct * & testStruct J'ai renommé certaines des variables, afin qu'elles aient plus de sens pour vous. – user2477533

+0

Je ne suis pas du tout expérimenté en C#, donc je m'excuse pour toute erreur. – user2477533

Répondre

1

tout d'abord changer le code C++ pour utiliser un seul niveau d'indirection:

extern "C" __declspec(dllexport) void Dll_TestArray(TestStruct &testStruct) 
{ 
    const int num = 15; 
    testStruct.num = num; 
    testStruct.intArray = new int[num]; 
    for (int i=0; i<num; i++) 
     testStruct.intArray[i] = i+1; 
} 

Du côté C# vous voulez ceci:

public struct TestStruct 
{ 
    public int num; 
    public IntPtr intArray; 
} 

[DllImport("testing.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern void Dll_TestArray(out TestStruct testStruct); 

public GetArray() 
{ 
    TestStruct testStruct; 
    Dll_TestArray(out testStruct); 
    int[] arr = new int[testStruct.num]; 
    Marshal.Copy(testStruct.intArray, arr, 0, arr.Length); 
    // need to call back to DLL to ask it to deallocate testStruct.intArray 
} 

Vous aurez également besoin d'exporter une fonction Cela libérera le tableau que vous avez alloué en utilisant le tas C++. Sinon, vous le ferez.

Peut-être une approche plus simple serait de changer la conception pour que l'appelant alloue le tampon.

+0

Merci beaucoup. En ce qui concerne la libération de la mémoire, etc, je suis heureux de prendre soin de cela. Je luttais juste pour récupérer les données que je voulais dans le tableau. – user2477533

Questions connexes