2009-12-05 13 views
10

Ich benutze eine alte API und muss den Zeiger einer Struktur an nicht verwalteten Code übergeben, der asynchron läuft.Fixieren einer aktualisierbaren Struktur vor der Übergabe an nicht verwalteten Code?

Mit anderen Worten, nach der Übergabe des struct-Zeigers an den nicht verwalteten Code kopiert der nicht verwaltete Code den Zeiger und kehrt sofort zurück. Der nicht verwaltete Code kann auf diese Struktur im Hintergrund in einem anderen Thread zugreifen. Ich habe keine Kontrolle über den nicht verwalteten Code, der in einem anderen Thread noch den Thread selbst ausgeführt wird.

Die fixierte {} -Anweisung kann nicht zum Pinnen verwendet werden, da sie nicht für async unmanaged Pinning entworfen wurde.

GCHandle kann nur Referenzen pinnen, daher muss die Struktur für die Verwendung von GCHandle eingerahmt werden. Ich habe es versucht, und es funktioniert. Das Hauptproblem dabei ist, dass Sie die Struktur aus verwaltetem Code nicht aktualisieren können. Um eine Struktur zu aktualisieren, müssen wir sie zuerst entpacken, dann aktualisieren, dann wieder boxen, aber ... oops ... box wieder?!? das bedeutet, dass der vorherige Zeiger im Speicher immer noch auf die alte, nicht aktuelle Struktur verweist, und die neue Struktur einen anderen Zeiger hat, und das bedeutet, dass ich einen neuen Zeiger auf den nicht verwalteten Code übergeben muss ... in meiner Anwendung nicht anwendbar Fall.

Wie kann ich eine Struktur in den Speicher ohne feste {} -Anweisung, und damit ich es aus verwaltetem Code ohne Änderung ändern, es Zeiger ist?

Danke.

Edit:

dachte nur ... ist es eine Möglichkeit, das übergeordnete Objekt an Pin, der die Struktur enthält, und erhalten dann den Zeiger des struct anstatt das Container-Objekt?

+0

Ich hätte erwartet, dass GCHandle hier die Lösung ist. Wenn alles fehlschlägt, können Sie den Speicher im nicht verwalteten Code zuordnen, sodass er nicht vom GC verschoben werden kann. – dtb

+1

Gute Frage. Ich denke, dass ich das in CLI/C++ getan habe, ich muss meine Arbeitsnotizen darauf überprüfen. Nicht sicher, ob es einen C# äquivalenten Mechanismus gibt. –

+0

Vielleicht führen Sie den Aufruf in einem eigenen Hintergrundthread aus, verwenden Sie fixed {} und verlassen Sie niemals den festen Block {} (zumindest solange der nicht verwaltete Code auf die Struktur zugreift)? –

Antwort

3

Ist unsicheren Code eine Option?

// allocate unmanaged memory 
Foo* foo = (Foo*)Marshal.AllocHGlobal(sizeof(Foo)); 

// initialize struct 
foo->bar = 0; 

// invoke unmanaged function which remembers foo 
UnsafeNativeMethods.Bar(foo); 
Console.WriteLine(foo->bar); 

// update struct 
foo->bar = 10; 

// invoke unmanaged function which uses remembered foo 
UnsafeNativeMethods.Qux(); 
Console.WriteLine(foo->bar); 

// free unmanaged memory 
Marshal.FreeHGlobal((IntPtr)foo); 

Dies kompiliert und keine Ausnahme werfen, aber ich weiß nicht eine nicht verwaltete Funktion zur Hand haben zu testen, ob es funktioniert.

Von MSDN:

Wenn AllocHGlobal LocalAlloc nennt, es passiert eine LMEM_FIXED Flagge, die die zugewiesenen Speicher an Ort und Stelle verriegelt werden verursacht. Außerdem ist der zugewiesene Speicher nicht mit Nullen gefüllt.

+0

hat Danke, funktioniert wie ein Zauber! Tatsächlich habe ich den AllocHGlobal und FreeHGlobal nicht verwendet, sondern nur den IntPtr genommen, den der GCHandle mir gegeben hat, und die Struktur über unsicheren Code aktualisiert. Verzeihen Sie mir für immer noch nicht als Antwort, weil ich auf mehr mögliche Antworten warten möchte, vielleicht gibt es bessere Lösungen ohne unsicheren Code. – DxCK

1

Anstatt zu fixieren, müssen Sie Marshal.StructureToPtr und Marshal.PtrToStructure verwenden, um die Struktur in Arbeitsspeicher zu marshalieren, der in nativem Code verwendbar ist.

+0

Es scheint, dass er die Struktur von C# aktualisieren will, nachdem es an nicht verwalteten Code übergeben wurde ... – dtb

+0

"StructureToPtr kopiert den Inhalt der Struktur in den zuvor zugewiesenen Speicherblock ..." Das ist nicht ganz das, was das OP gefragt. Er möchte, dass der native Code den Speicher des Elements direkt manipuliert. Ich nehme an, das ist aus Leistungsgründen ... Vielleicht irre ich mich. –

+0

Sie können nicht. Sie müssen es in einen Marshaltzugewiesenen Speicherblock kopieren, in C++ aktualisieren und dann zurück auf den Stack kopieren. Es gibt keinen direkten Weg, dies anders zu machen. Wenn Sie dies nicht tun möchten, verwenden Sie am besten C++/CLI. –

0

Struct Beispiel:

[StructLayout(LayoutKind.Sequential)] 
public struct OVERLAPPED_STRUCT 
{ 
    public IntPtr InternalLow; 
    public IntPtr InternalHigh; 
    public Int32 OffsetLow; 
    public Int32 OffsetHigh; 
    public IntPtr EventHandle; 
} 

Wie es auf die Struktur an Pin und es verwenden:

OVERLAPPED_STRUCT over_lapped = new OVERLAPPED_STRUCT(); 
// edit struct in managed code 
over_lapped.OffsetLow = 100; 
IntPtr pinned_overlap_struct = Marshal.AllocHGlobal(Marshal.SizeOf(over_lapped)); 
Marshal.StructureToPtr(over_lapped, pinned_overlap_struct, true); 

// Pass pinned_overlap_struct to your unmanaged code 
// pinned_overlap_struct changes ... 

// Get resulting new struct 
OVERLAPPED_STRUCT nat_ov = (OVERLAPPED_STRUCT)Marshal.PtrToStructure(pinned_overlap_struct, typeof(OVERLAPPED_STRUCT)); 
// See what new value is 
int offset_low = nat_ov.OffsetLow; 
// Clean up 
Marshal.FreeHGlobal(pinned_overlap_struct); 
6

Verwendung von gepinnten Speicher in diesem Fall ist keine gute Idee, da der Speicher für die Struktur für eine lange Zeit gültig sein muss. GCHandle.Alloc() wird die Struktur boxen und auf dem Heap speichern. Wenn es festgenagelt wird, wird es eine langfristige Belastung für den Müllsammler sein, da es ständig einen Weg um den Felsen in der Straße finden muss.

Die einfache Lösung besteht darin, Speicher für die Struktur im nicht verwalteten Speicher zuzuweisen. Verwenden Sie Marshal.SizeOf(), um die Größe der Struktur abzurufen, und Marshal.AllocCoTaskMem(), um den Speicher zuzuordnen. Dadurch erhalten Sie den Zeiger, den Sie an den nicht verwalteten Code übergeben müssen. Initialisieren Sie den Speicher mit Marshal.StructureToPtr(). Und lesen Sie Updates zu der Struktur, die vom nicht verwalteten Code mit PtrToStructure() geschrieben wurde.

Wenn Sie dies häufig machen, kopieren Sie ständig die Struktur. Das könnte je nach Größe der Struktur teuer sein. Um dies zu vermeiden, verwenden Sie einen unsicheren Zeiger, um direkt auf den nicht verwalteten Speicher zuzugreifen. Einige grundlegende Syntax:

using System; 
using System.Runtime.InteropServices; 

class Program { 
    unsafe static void Main(string[] args) { 
    int len = Marshal.SizeOf(typeof(Test)); 
    IntPtr mem = Marshal.AllocCoTaskMem(len); 
    Test* ptr = (Test*)mem; 
    ptr->member1 = 42; 
    // call method 
    //.. 
    int value = ptr->member1; 
    Marshal.FreeCoTaskMem(mem); 
    } 
    public struct Test { 
    public int member1; 
    } 
} 
+0

Was ist der Unterschied zwischen der Verwendung all dieser Methoden: Marshal.AllocCoTaskMem und Marshal.FreeCoTaskMem VS Marshal.AllocHGlobal und Marshal.FreeHGlobal, sizeof VS Marshal.SizeOf und was ist die richtige Methoden, die ich in meinem Fall verwenden sollte und warum? Tnx. – DxCK

+0

Ich kann die Antwort nicht in ein Kommentarfeld passen.Warum beginnst du nicht einen neuen Thread mit dieser Frage? –

+0

http://stackoverflow.com/questions/1887288/marshal-allochglobal-vs-marshal-alloccotaskmem-marshal-sizeof-vs-sizeof – DxCK

0

Wie wäre es die Struktur schließen eine ActOnMe() Schnittstelle und Verfahren so etwas wie:

 
delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2); 
interface IActOnMe<TT> {ActOnMe<T>(ActByRef<TT,T> proc, ref T param);} 
struct SuperThing : IActOnMe<SuperThing> 
{ 
    int this; 
    int that; 
    ... 
    void ActOnMe<T>(ActByRef<SuperThing,T>, ref T param) 
    { 
    proc(ref this, ref param); 
    } 
} 

Da die Delegierten einen allgemeinen Parameter durch Verweis nimmt, sollte es in den meisten Fällen möglich sein, Vermeiden Sie den Overhead beim Erstellen von Closures, indem Sie einen Delegaten an eine statische Methode übergeben, zusammen mit einem Verweis auf eine Struktur, um Daten zu oder von dieser Methode zu übertragen. Wenn eine bereits eingerahmte Instanz von SuperThing in IActOnMe<SuperThing> umgewandelt wird und ActOnMe<T> aufgerufen wird, werden die Felder dieser eingerahmten Instanz zum Aktualisieren offengelegt, im Gegensatz zum Erstellen einer weiteren Kopie von ihnen, wie sie bei einer Typumwandlung in die Struktur auftreten würde.