繁体   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