簡體   English   中英

DEVMODE結構中C#StructLayout.Explicit的對齊錯誤

[英]Alignment error with C# StructLayout.Explicit in DEVMODE structure

我正在嘗試使用EnumDisplaySettings ,它使用DEVMODE結構作為結果結構。 DEVMODE結構在內部使用了一些聯合,這使得在C#中使用起來更加復雜。 聯合用於對顯示器或打印機進行編號。 StructLayout.Explicit中的 FieldOffsets應該可以使用工會。

下面是從pinvoke.net復制的結構。 顯然,其他一些結構也存在問題,只需將其構造為StructLayout.Sequential即可解決聯合,並創建了兩個結構,一個用於顯示,一個用於打印機。

拋出的異常是字段偏移量70,它表明該字段未對齊或與另一個字段重疊。 這是我不了解的,當然字段可以與使用的顯式布局重疊,並且字段偏移量68之前的字段也很短,不能與字段偏移量70重疊。 這就是Microsoft定義的結構的工作方式。 當場偏移從70移到72時它起作用

因此,我實際上一般不會解決我的問題,但是我對這里發生的情況感興趣。

[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;
}

70是正確的偏移量。 對於CharSet = CharSet.Auto,它是102,這是您應該始終喜歡的值。

問題是代碼不必要地使用了[FieldOffset]。 這不僅可以設置封送結構中字段的偏移量,還可以降低托管結構中字段的偏移量。

那是個大問題,70無效,因為這會使內存中的字符串未對齊。 .NET內存模型要求在32位模式下,引用類型引用必須對齊到4的倍數的地址。垃圾回收器確實討厭未對齊的字段,對象引用需要能夠原子更新和未對齊引用不能保證這一點。 它們可能跨越L1高速緩存行,這需要兩個內存總線周期來設置該值。 導致撕裂 ,僅看到更新的一部分,這是無法調試的問題。

刪除所有[FieldOffset]屬性或從參考源中復制/粘貼。 另一個優點是,如果您的程序以64位模式運行,您會感覺它仍然可以正常運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM