2014-12-27 4 views
10

Ich habe versucht, ein Handle zu einem Strukturtyp zu erstellen, weil ich einen gepinnten Zeiger darauf brauche, aber ich bekomme den Fehler "Objekt enthält nicht primitive oder nicht blitfähig Daten "Wie ordne ich GCHandle der Struktur zu, wenn die Struktur bool enthält

Meine Struktur sieht wie folgt aus:

[StructLayout(LayoutKind.Sequential)] 
public struct MyStruct 
{ 
    [MarshalAs(UnmanagedType.U1)] 
    public bool Test; 
} 

Nun, wenn ich rufe,

var mystruct = new MyStruct(); 
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned); 

ich den Fehler" Objekt enthält nicht-primitive oder nicht-blitfähig Daten ". Jetzt verstehe ich, dass das Bool-Feld ein nicht-blitbarer Typ ist. Aber ich hatte den Eindruck, dass ich durch Hinzufügen des MarshalAs-Attributs dem Marshaller sagen konnte, wie man den Typ umwandelt. (Ich habe auch versucht UnmanagedType.Bool)

Diese Struktur muss global definiert werden, weil es in meiner Klasse benötigt wird. Der einzige Grund, warum ich den Zeiger brauche, ist, weil ich eine nicht verwaltete API habe, die diese Struktur als Zeiger übergeben muss. Dann muss ich diese Struktur in einem Callback bekommen und Mitglieder lesen/aktualisieren.

Das ist also das Basisszenario.

  1. Struktur ist global angelegt in einer verwalteten Klasse
  2. Zeiger auf Struktur erhalten wird
  3. Zeiger auf die Struktur in die API übergeben
  4. Die API eine statische Methode Rückrufe, wo ich muss dann bekommen Meine Struktur und lese/aktualisiere Mitglieder.

Ich versuchte Marshal.StructureToPtr zu verwenden, aber das schafft nur eine Kopie, so dass, wenn in meinem verwalteten Klasse I das Element aktualisieren, wenn der Rückruf ausgelöst wird, wird der aktualisierte Wert nicht da.

Weiß jemand, wie ich einen gepinnten Zeiger auf meine Struktur bekommen kann, damit ich die öffentlichen Mitglieder lesen/ändern kann und sie im Rückruf verfügbar haben?

Dank

+2

hier ist die Liste der blitfähig Typen http://msdn.microsoft.com/en-us/library/75dwhxf7.aspx – Mayank

Antwort

13

Sie haben mehr als ein Problem hier. Die Verwendung eines struct ist sehr zu empfehlen. Es wird vor dem Aufruf von GCHandle.Alloc() und eingerahmt, das Boxed-Objekt wird fixiert. Sie können keine Updates über Ihre mystruct Variable sehen. Verwenden Sie stattdessen eine Klasse.

Und vermeiden Sie bool, es ist ein nicht blittierbarer Typ aufgrund seiner sehr variablen Implementierung. Es ist 4 Bytes in C, 1 Byte in C++, 2 Bytes in COM. Machen Sie es einfach byte statt. Sie können eine Eigenschaft schreiben, um sie zurück zu einem Bool zu bekommen. So

:

[StructLayout(LayoutKind.Sequential)] 
public class MyStruct 
{ 
    private byte _test; 
    public bool Test { 
     get { return _test != 0; } 
     set { _test = value ? 1 : 0; } 
    } 
} 
+0

Ich habe es tatsächlich auch als Klasse versucht, aber ich war aufgelegt, um es zu zwingen, einen Bool-Typ zu verwenden. Ich lese die Antwort von @hvd, und er hat Recht. Es sieht so aus, als würde MarshalAs dort nichts tun. Ich hatte es versucht, nur um zu sehen, ob ich eine 'GCHandle' kreieren konnte, und ich konnte es, aber ich mochte die Idee nicht. Ich markiere dies als Antwort, weil ich nicht überlegt habe, eine Eigenschaft zu verwenden, um das Mitglied zu umhüllen. Ich werde es versuchen! Vielen Dank! –

+0

ECMA-335 11.7.4 sagt "Boolean. 4-Byte-Integer-Wert, wo ein Wert ungleich Null Wert für wahr, und 0 steht für FALSE." Das klingt für mich nicht sehr variabel. Ich gehe davon aus, dass es ein Versehen war oder vielleicht wurde entschieden, weil die Semantik anders ist als normale Integer-Typen und keine technische Entscheidung. –

7

Du hast Recht, dass Sie die Einweiser wie doch sagen, die Art Marschall.

Aber das wird dir nichts nützen, wenn du dann versuchst, den Marshaller zu umgehen.

Sie müssen entscheiden, ob Sie den Marshaller verwenden möchten oder ob der nicht verwaltete Code direkt in den verwalteten Speicher schreiben soll.

Wenn Sie die Einweiser verwenden möchten:

Im Allgemeinen ist eine gute Möglichkeit, dies zu umgehen ist es in beiden Richtungen zu verwenden. Sie können Marshal.StructureToPtr (wie Sie gefunden haben) verwenden, rufen Sie die externe Funktion auf und verwenden Sie dann Marshal.PtrToStructure, um es wieder in Ihre verwaltete Darstellung zu konvertieren.

Oder Sie können Methoden verwenden, die so eingerichtet sind, dass das Marshalling automatisch erfolgt, ohne dass Sie dies manuell festlegen müssen. Wenn Sie zum Beispiel eine native Methode aufrufen, die einen Parameter ref MyStruct annimmt, wird dies passieren.

Wenn Sie nicht möchten, dass die Einweiser verwenden:

Sie alle Arten nicht verwenden, die Rangierung erfordern. Wie Hans Passant kommentiert, verwenden Sie stattdessen einen anderen Typ, byte wäre wahrscheinlich eine gute Wahl.

(Ich werde verzichten Kommentierung über die Vor- und Nachteile von structs hier, mit der Ausnahme, dass die Punkte bereits über es wert sind Lesen und Verstehen.)

Verwandte Themen