2016-08-02 23 views
1

Ich habe zwei C++ - Strukturen wie unten gezeigt. Ich muss die Struktur beim Aufrufen einer DLL-Methode aus C# abrufen.Marshalling verschachtelte Strukturen

sie beispielsweise in C, wie unten ermöglicht definieren ++ Code:

struct A 
{ 
    int count; 
    struct B; 
} 

struct B 
{ 
    char* id; 
    char* name; 
} 

C++ Code kehrt die folgende Methode

A* GetData(); 

Verfahren, das ich brauche von C# nennen hat die folgende Signatur:

IntPtr GetData(); 

Diese Methode gibt den Zeiger auf Struktur A Füllung Anzahl in Struktur A, ID und Namen von struct B.

In C# definiere ich diese Strukturen als Klassen:

[StructLayout(LayoutKind.Sequential)] 

class A 
{ 

    public int count; 
    public IntPtr B; 
} 

[StructLayout(LayoutKind.Sequential)] 

class B 
{ 

    public string id; 
    public string name; 
} 

Ich habe ein C++ DLL erstellt von C# zu nennen. Wenn ich versuche, Daten aus der verschachtelten Struktur (A->B->id) zu lesen, erhalte ich einen Leseverletzungsfehler (AccessViolationException).

Wie kann ich die verschachtelte Struktur zuordnen, damit ich sie in der C# -Methode lesen kann?

Mein C# -Code ist als unten

[DllImport("Win32Project.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] 

public static extern IntPtr GetData(); 

A setting = new A(); 

setting.B = new IntPtr(); 

IntPtr deviceSettingptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A))); 

IntPtr settingsInfoptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(B))); 

Marshal.StructureToPtr(setting, deviceSettingptr, false); 

Marshal.StructureToPtr(setting.B, settingsInfoptr, true); 

setting.B= settingsInfoptr; 

deviceSettingptr = GetData(); 

setting = (A)Marshal.PtrToStructure(deviceSettingptr, typeof(A)); 

B info = (B)Marshal.PtrToStructure(setting.B, typeof(B)); 

Console.WriteLine(string.Format("Setting count={0}", setting.count)); 
Console.WriteLine(string.Format("Setting id={0}, Setting name={1}", info.id, info.name)); 

Wie greife ich auf die Mitglieder der Struktur B id und Namen haben?

+0

Warte, also ist 'A.b' ein Zeiger auf' B' oder nur 'B'? – Luaan

+1

Das A.B Mitglied ist eine Struktur, kein Zeiger. Sie müssen es stattdessen als B deklarieren. Die Strings in B sind ein sehr bedeutendes Speicherverwaltungsproblem, jemand muss sie wieder freigeben. Der pinvoke-Marshaller kann das nicht, Sie müssen sie daher als IntPtr deklarieren und Marshal.PtrToStringAnsi() verwenden. Wie wirst du die Erinnerung loslassen, nun, viel Glück. Ähnliches gilt für den Rückgabewert von GetData(), auch hohe Chancen, dass dies ein dangling pointer ist. Verbessern Sie den C++ - Code zuerst, es ist nicht verwendbar wie es ist. –

+0

Ich deklarierte Zeichenfolgen in Struktur B als IntPtr und verwendet unten, um die Daten zurück zu erhalten. Zeichenfolgen-ID = Marshal.PtrToStringAnsi (info.id); Aber das ist eine leere Zeichenfolge. –

Antwort

0

Der Code, den Sie gepostet haben, sieht etwas komisch aus. Alles vor der Zeile

deviceSettingptr = GetData(); 

scheint irrelevant. Sie haben nicht gezeigt, was GetData() tut, aber es scheint, dass diese Methode einen Zeiger auf eine Struktur zurückgibt. Also zuerst lesen Sie die Struktur (vermutlich ein A):

deviceSettingptr = GetData(); 
setting = (A)Marshal.PtrToStructure(deviceSettingptr, typeof(A)); 

(An dieser Stelle alles, was Sie auf die Variable setting tat, bevor obsolet wird).

Und jetzt wollen Sie (wahrscheinlich) eine Struktur vom Typ B aus dem Speicher durch setting.B darauf zu lesen:

B info = (B)Marshal.PtrToStructure(setting.B, typeof(B)); 

In Ihrem Code verwendet man settingsInfoptr stattdessen das war nicht richtig eingestellt.

Jetzt können Sie die Eigenschaften zugreifen id und name von info:

Console.WriteLine(string.Format("Setting count={0}", setting.count)); 
Console.WriteLine(string.Format("Setting id={0}, Setting name={1}", info.id, info.name)); 

Beachten Sie, dass wahrscheinlich jemand loslassen sollte die wieder von GetData() zugewiesenen Speicher.

+0

B info = (B) Marshal.PtrToStructure (setting.B, typeof (B)); Console.WriteLine (string.Format ("Einstellungs-ID = {0}, Einstellungsname = {1}", info.id, info.name)); info.id und info.name gibt den Zeigerwert und nicht den Zeichenfolgenwert an. –

+0

Ja, ich habe es jetzt –