2010-12-02 39 views
2

Je dois appeler une DLL C++ en C#. Et l'en-tête de la dll est le suivant (simplifié):Comment marshaler un tableau de structure En C#?

// En-tête de C++

struct vector 
{ 
    float x; 
    float y; 

    vector() 
    {} 

    vector(float x0, float y0) 
    { 
     x = x0; 
     y = y0; 
    } 
}; 

struct unmanaged_struct 
{ 
    int int_var; 
    float float_var; 
    char* chars_var; 
    vector vector_var; 

    unmanaged_struct(int i, float f, char* ch, float vec_x, float vec_y) 
    { 
     int_var = i; 
     float_var = f; 
     chars_var = ch; 
     vector_var = vector(vec_x, vec_y); 
    } 
}; 

// Cette fonction permet de transmettre toutes les valeurs variables de l'instance de struct

extern "C" __declspec(dllexport) void unmanagedstruct_summary(unmanaged_struct* us_list, int length); 

Et je définissais la classe suivante en C#

// CSharp

[StructLayout(LayoutKind.Sequential)] 
public class Vector 
{ 
    public float x; 
    public float y; 

    public Vector(float f1, float f2) 
    { 
     x = f1; 
     y = f2; 
    } 
} 

[StructLayout(LayoutKind.Sequential)] 
public class UnmanagedStruct 
{ 
    public int int_var; 
    public float float_var; 
    public string char_var; 
    public Vector vector_var; 

    public UnmanagedStruct(int i, float f, string s, Vector vec) 
    { 
     this.int_var = i; 
     this.float_var = f; 
     this.char_var = s; 
     this.vector_var = vec; 
    } 
} 

class UnmanagedDllCallTest 
{ 
    [DllImport("unmanageddll.dll", EntryPoint = "unmanagedstruct_summary")] 
    public static extern void unmanagedstruct_summary([Out]UnmanagedStruct[] usList, int length);  

    static void Main(string[] args) 
    { 

     UnmanagedStruct[] usList = new UnmanagedStruct[1]; 
     usList[0] = new UnmanagedStruct(1, 1.0f, "aa", new Vector(10, 1));  
     usList[1] = new UnmanagedStruct(2, 2.0f, "ba", new Vector(20, 2)); 
     UnmanagedDllCallTest.unmanagedstruct_summary(usList, 2); 
} 

Et la sortie est comme suit:

résumé unmanaged_struct:

1.12104e-044

Exception non gérée: System.AccessViolationException: Tentative de lecture ou écriture protégée mémoire. C'est souvent une indication que l'autre mémoire est corrompue. à callunmanageddll.UnmanagedDllCallTest.unmanagedstruct_summary (UnmanagedStr UCT [] usList, longueur Int32) à callunmanageddll.Program.Main (String [] args ) dans c: \ users \ dynaturtle \ docume nts \ studio visuel 2010 \ Projects \ callunmanageddll \ callunmanageddll \ Program.cs: lin e 68

le dll C++ est OK comme je l'ai test écrit en C++ et la fonction fonctionne bien. J'ai lu this thread mais il semble que la solution n'a pas fonctionné dans mon cas. Aucune suggestion? Merci d'avance!

Répondre

1

Premièrement: Vector et UnmanagedStruct devraient être des structures, pas des classes.

+0

Mais quelle est la différence entre les structures et la classe? –

5

Utilisez Marshal.PtrToStructure. Il y a un échantillon here.

Vous devrez donc changer la signature de la méthode de la matrice de structure à IntPtr. Cependant, vous devez connaître la taille de la mémoire tampon qui est transmise.

public struct Vector 
{ 
    public float x; 
    public float y; 

} 

public struct UnmanagedStruct 
{ 
    public int int_var; 
    public float float_var; 
    public string char_var; 
    public Vector vector_var; 

} 

class UnmanagedDllCallTest 
{ 
    [DllImport("unmanageddll.dll", EntryPoint = "unmanagedstruct_summary")] 
    public static extern void unmanagedstruct_summary([Out] IntPtr ptr, int length);  

    static void Main(string[] args) 
    { 

    for(int i=0; i<length; i++) 
    { 
     UnmanagedStruc st; 
     Marshal.PtrToStructure(ptr, st); 
     // increment ptr and move forward 
    } 

} 
+0

PtrToStructure est utilisé pour une seule structure, mais je passerais un tableau de structure à la fonction d'exportation dll –

+0

J'ai mis un exemple de code. C'est la question de la boucle et de la modification de la valeur du pointeur. – Aliostad

0

JIC Je partagerais mon approche. Peut-être que ce n'est pas une réponse attendue à un moment donné, je passe un moment à résoudre mon problème.

J'ai la structure suivante pour exposer certaines données de DLL.

//C++ code 
    struct State 
    { 
     const wchar_t * name; 
     unsigned int state; 
    }; 

APIENTRY bool get_states(H_PRCSR, MacroState *, const int sz); //pay attention, function accepts already allocated array and size for it 

Pour accepter ces données de C++ je peux le faire de cette façon

std::vector<State> states(desired_size); 
get_states(hparser, &states[0], states.size()); 

Pour faire la même chose sur C# J'utilisé la manière suivante

//C# 
[StructLayout(LayoutKind.Sequential)] 
public struct Status 
{ 
    public IntPtr name; 
    public uint state; 

    public string getName() 
    { 
    if (name == IntPtr.Zero) return "<no-value>"; 
    return Marshal.PtrToStringUni(name); 
    } 
} 

//And import function... 

[DllImport(dll, CallingConvention = CallingConvention.Winapi)] 
private static extern bool get_states(IntPtr p, [Out]MacroFlag[] flags, int flags_size); 

//And simple decoder 
public static Status[] getAll(IntPtr p, int size) 
{ 
    var results = new Status[size]; 

    get_states(p, results, size); 

    return results; 
} 

Comme, j'ai vu sont différents approches pour le faire.C'est l'un d'eux. Et cela fonctionne pour moi. Peut-être, ce post ne résoudra pas le problème mais sera un bon point de départ