2017-04-21 4 views
-1

Ich versuche, die EnumDisplaySettings zu verwenden, die die DEVMODE-Struktur als Ergebnisstruktur verwendet. Die DEVMODE Struktur verwendet intern einige Unionen, was die Verwendung in C# etwas komplizierter macht. Die Vereinigungen werden entweder zum Aufzählen von Anzeigen oder Druckern verwendet. FieldOffsets in einem StructLayout.Explicity sollten den Trick, Gewerkschaften zu verwenden, tun.Ausrichtungsfehler mit C# StructLayout.Explicit in DEVMODE-Struktur

Unten ist die Struktur, die von pinvoke.net kopiert wurde. Offensichtlich hatten einige andere auch Probleme mit dieser Struktur und lösten die Verbindungen, indem sie einfach StructLayout.Sequential erstellten und zwei Strukturen erstellten, eine für Anzeigen und eine für Drucker.

Die Ausnahme, die ausgelöst wird, ist Feld Offset 70, die besagt, dass das Feld nicht ausgerichtet oder durch ein anderes Feld überlappt ist. Und das ist, was ich nicht verstehe, natürlich können Felder mit expliziten Layout verwendet werden überlagert und auch das Feld vor dem Feld Offset 68 ist kurz, die nicht in Fieldoffset 70 überlappen kann. So funktioniert die Struktur wie von Microsoft definiert. Beim Verschieben des Fieldoffset von 70 auf 72 funktioniert es.

So bin ich wirklich nicht in der Reparatur meines Problems im Allgemeinen, aber ich interessiere mich für den Hintergrund dessen, was hier passiert.

[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] 
public struct DEVMODE3 
{ 
    public const int CCHDEVICENAME = 32; 
    public const int CCHFORMNAME = 32; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] 
    [System.Runtime.InteropServices.FieldOffset(0)] 
    public string dmDeviceName; 
    [System.Runtime.InteropServices.FieldOffset(32)] 
    public Int16 dmSpecVersion; 
    [System.Runtime.InteropServices.FieldOffset(34)] 
    public Int16 dmDriverVersion; 
    [System.Runtime.InteropServices.FieldOffset(36)] 
    public Int16 dmSize; 
    [System.Runtime.InteropServices.FieldOffset(38)] 
    public Int16 dmDriverExtra; 
    [System.Runtime.InteropServices.FieldOffset(40)] 
    public uint dmFields; 

    [System.Runtime.InteropServices.FieldOffset(44)] 
    Int16 dmOrientation; 
    [System.Runtime.InteropServices.FieldOffset(46)] 
    Int16 dmPaperSize; 
    [System.Runtime.InteropServices.FieldOffset(48)] 
    Int16 dmPaperLength; 
    [System.Runtime.InteropServices.FieldOffset(50)] 
    Int16 dmPaperWidth; 
    [System.Runtime.InteropServices.FieldOffset(52)] 
    Int16 dmScale; 
    [System.Runtime.InteropServices.FieldOffset(54)] 
    Int16 dmCopies; 
    [System.Runtime.InteropServices.FieldOffset(56)] 
    Int16 dmDefaultSource; 
    [System.Runtime.InteropServices.FieldOffset(58)] 
    Int16 dmPrintQuality; 

    [System.Runtime.InteropServices.FieldOffset(44)] 
    public POINTL dmPosition; 
    [System.Runtime.InteropServices.FieldOffset(52)] 
    public Int32 dmDisplayOrientation; 
    [System.Runtime.InteropServices.FieldOffset(56)] 
    public Int32 dmDisplayFixedOutput; 

    [System.Runtime.InteropServices.FieldOffset(60)] 
    public short dmColor; // See note below! 
    [System.Runtime.InteropServices.FieldOffset(62)] 
    public short dmDuplex; // See note below! 
    [System.Runtime.InteropServices.FieldOffset(64)] 
    public short dmYResolution; 
    [System.Runtime.InteropServices.FieldOffset(66)] 
    public short dmTTOption; 
    [System.Runtime.InteropServices.FieldOffset(68)] 
    public short dmCollate; // See note below! 
    [System.Runtime.InteropServices.FieldOffset(70)] 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] 
    public string dmFormName; 
    [System.Runtime.InteropServices.FieldOffset(102)] 
    public Int16 dmLogPixels; 
    [System.Runtime.InteropServices.FieldOffset(104)] 
    public Int32 dmBitsPerPel; 
    [System.Runtime.InteropServices.FieldOffset(108)] 
    public Int32 dmPelsWidth; 
    [System.Runtime.InteropServices.FieldOffset(112)] 
    public Int32 dmPelsHeight; 
    [System.Runtime.InteropServices.FieldOffset(116)] 
    public Int32 dmDisplayFlags; 
    [System.Runtime.InteropServices.FieldOffset(116)] 
    public Int32 dmNup; 
    [System.Runtime.InteropServices.FieldOffset(120)] 
    public Int32 dmDisplayFrequency; 
} 

Antwort

2

70 ist der korrekte Offset. Es ist 102 für CharSet = CharSet.Auto, das du immer bevorzugen solltest.

Das Problem ist, dass der Code [FieldOffset] unnötigerweise verwendet. Dadurch wird nicht nur der Offset des Feldes in der Marshalled-Struktur festgelegt, sondern auch der Offset des Felds in der verwalteten Struktur.

Und das ist ein großes Problem, 70 ist nicht gültig, da dies die Zeichenfolge im Speicher falsch ausrichtet. Das .NET-Speichermodell erfordert, dass Referenztypreferenzen im 32-Bit-Modus an einer Adresse ausgerichtet werden müssen, die ein Vielfaches von 4 ist. Der Garbage Collector hasst wirklich fehlausgerichtete Felder, Objektverweise müssen atomar aktualisiert und falsch ausgerichtet sein Referenzen können diese Garantie nicht haben. Sie können die L1-Cachezeile überspannen, die zwei Speicherbuszyklen erfordert, um den Wert festzulegen. Ursachen Zerreißen, nur einen Teil eines Updates zu sehen, ein Problem, das nicht zu debuggen ist.

Löschen Sie alle [FieldOffset] -Attribute oder kopieren/einfügen von der Reference Source. Ein weiterer Vorteil ist, dass Sie sich gut fühlen können, wenn das Programm im 64-Bit-Modus läuft.

+0

Danke für die Rückmeldung und den Link. Es ist immer traurig, dass die Methoden verfügbar sind, aber auf intern gesetzt sind. Die Struktur enthält nur die Aufzählung für die Anzeige, nicht für Drucker. Wie würdest du die Gewerkschaften dann lösen -> 2 verschiedene Strukturen? Was ist, wenn ich über alle Elemente aufzählen möchte? – msedi

+1

Es ist eine hässliche Mutter. Die Gewerkschaften versuchen, die Unterschiede zwischen dem Devmode für einen Drucker und einem für eine Anzeige zu dokumentieren. Der eine, mit dem ich verlinkt bin, stimmt mit dem Union-Flavor für eine Anzeige überein, was Sie versuchen zu verwenden. Wenn Sie eine für einen Drucker benötigen, fügen Sie einfach eine weitere Deklaration hinzu, Windows interessiert sich nicht für den C# -Strukturnamen. –

+0

In Ordnung. Ich hatte gehofft, mehr "generische" Lösung zu haben, aber das bedeutet, dass ich zweimal mit verschiedenen Strukturen aufzählen muss und dann entscheiden muss, was ich bekommen habe. Das ist für mich in Ordnung, zeigt aber auch, dass einige Strukturen in Windows nicht sehr gut (oder lang anhaltend) definiert wurden. – msedi