2016-11-08 7 views
1

Ich arbeite an einem Projekt, das in C++ geschriebene DLL und einen C# -Code enthält. Lassen Sie uns sagen, dass diese DLL eine Funktion hat:Umgang mit Zeigern zu Struktur in C#

MyStruct* GetPointer(); // returns pointer to MyStruct structure 

MyStruct Struktur sieht wie folgt aus:

struct MyStruct 
{ 
    OtherStruct1 *data1; 
    OtherStruct2 *data2; 
}; 

Und OtherStruct1 und OtherStruct2 Strukturen wie folgt aussehen:

struct OtherStruct1 
{ 
public: 
    double x; 
    char y; 
}; 

struct OtherStruct2 
{ 
public: 
    float a; 
    float b; 
    float c; 
    float d; 
}; 

Meine Frage ist - was ist Der beste Weg, um alle diese Zeiger in einem C# -Code zu behandeln? Mit "Handhabung" meine ich Lese- und Speichervorgänge. Strukturen in C# können nicht einfach Zeigervariablen enthalten. Was soll ich machen? Was ist der eleganteste Weg?

+0

Sind Sie erwarten, dass die Objekte, die im C# -Code geändert werden, auch den C++ - Code aktualisieren, der auf dieselben Zeiger schaut? Ist es in Ordnung, wenn Sie vor dem Zeigerobjekt einen Aufruf wie [Marshal.StructureToPtr'] (https://msdn.microsoft.com/en-us/library/dn261468 (v = vs.110) .aspx) tätigen müssen wird aktualisiert? –

+0

Ich erwarte eine Möglichkeit, Werte von Objekten, die von DLL angegeben werden, direkt im Speicher zu ändern, damit der C++ - Code an diesen Objekten weiterarbeiten kann, die durch C# -Code geändert wurden. Es ist so, als ob DLL Zeiger gibt, C# füllt sie mit Anfangsdaten und DLL arbeitet daran. Dann liest der C# -Code die von der DLL modifizierten Daten und die Ergebnisse auf der Leinwand. –

+0

Ist es möglich, 'GetPointer' zu einer C# -Methode zu machen, so dass der verwaltete Code derjenige ist, der den gemeinsamen Speicherbereich erzeugt? Und/Oder können Sie ein [C++/Cli] (https://msdn.microsoft.com/en-us/library/68td296t.aspx) Shim verwenden, um die Lücke zu überbrücken? –

Antwort

1

Sie können das Microsoft (jetzt Open Source) PInvoke Interop Assistant Tool verwenden, um Ihren C/C++ Code in C# oder VB zu konvertieren. Starten Sie Ihren Beispielcode gibt:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct MyStruct { 

    /// OtherStruct1* 
    public System.IntPtr data1; 

    /// OtherStruct2* 
    public System.IntPtr data2; 
} 

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct OtherStruct1 { 

    /// double 
    public double x; 

    /// char 
    public byte y; 
} 

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
public struct OtherStruct2 { 

    /// float 
    public float a; 

    /// float 
    public float b; 

    /// float 
    public float c; 

    /// float 
    public float d; 
} 

public partial class NativeMethods { 

    /// Return Type: MyStruct* 
    [System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetPointer")] 
public static extern System.IntPtr GetPointer() ; 

} 

Ersetzen Sie „Unknown“ im DllImportAttribute mit Ihrem DLL-Namen, und stellen Sie sicher, dass es im Projekt verwiesen wird. Sie sollten jetzt in verwaltetem Code auf Ihre Strukturen zugreifen können.

Anschließend müssen Sie die Methoden im System.Runtime.InteropServices.Marshal-Namespace verwenden, um von/in den Speicher zu lesen/schreiben. Der folgende Codeausschnitt zeigt, wie Sie Ihre GetPointer Funktion benutzen, um Ihre nicht verwalteten Struktur in eine Eins geschafft zu bekommen:

IntPtr myPtr = NativeMethods.GetPointer(); Call native code to retrieve pointer to unmanaged memory where the struct lives 
MyStruct myStruct = new MyStruct(); // Create a new managed struct 
myStruct = Marshal.PtrToStructure<MyStruct>(myPtr); 

Und hier ist, wie Sie eine verwaltete Struktur an eine nicht verwaltete Methode übergeben würde:

MyStruct myStruct = new MyStruct(); // Create the managed struct 
myStruct.data1 = new OtherStruct1(); // Create a managed OtherStruct1 
myStruct.data2 = new OtherStruct2(); // Create a managed OtherStruct2 
IntPtr myStructPtr = Marshal.AllocHGlobal(Marshal.SizeOf<MyStruct>()); // Allocate unmanaged memory for the struct 
Marshal.StructureToPtr<MyStruct>(myStruct, myStructPtr, false); 
try 
{ 
    NativeMethodThatUsesMyStructPtr(myStructPtr); 
} 
finally 
{ 
    Marshal.FreeHGlobal(myStructPtr); // *** You have to free unmanaged memory manually, or you will get a memory leak! *** 
} 
+1

Danke, das ist die elegantere Art, ohne "unsicheren" Block zu verwenden :) –

+0

Ich habe bearbeitet, um zu zeigen, wie man den nicht verwalteten Speicher freigibt, nachdem man damit fertig ist. Dies ist ein Muss, oder Sie werden ein Speicherleck bekommen! – BitBot

+0

Shared Memory ist frei mit der Verwendung der DLL Clear() Funktion :) Aber trotzdem, das ist ziemlich nützliches Wissen für die Zukunft :) –

2

Ok, ich habe einen Weg gefunden, es zu tun.

[StructLayout(LayoutKind.Sequential)] 
struct MyStruct 
{ 
    OtherStruct1 *data1; 
    OtherStruct2 *data2; 
}; 

[StructLayout(LayoutKind.Sequential)] 
struct OtherStruct1 
{ 
public: 
    double x; 
    char y; 
}; 

[StructLayout(LayoutKind.Sequential)] 
struct OtherStruct2 
{ 
public: 
    float a; 
    float b; 
    float c; 
    float d; 
}; 

Und dann:

unsafe 
{ 
    MyStruct *tmp = (MyStruct*) GetPointer(); 
    tmp->data2[0].a = 1.0F; 
} 


[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)] 
    unsafe public static extern MyStruct* GetPointer(); 

Arbeiten wie ein Charme. :)

Verwandte Themen