我正在尝试使用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;
}发布于 2017-04-21 15:49:31
70是正确的偏移量。对于CharSet = CharSet.Auto,它是102,你应该总是喜欢它。
问题是代码不必要地使用了FieldOffset。这不仅设置了封送结构中字段的偏移量,而且还降低了托管结构中字段的偏移量。
这是一个大问题,70是无效的,因为这错误地对齐了内存中的字符串。.NET内存模型要求,在32位模式下,引用类型引用需要对齐到4倍的地址。垃圾收集器确实讨厌对齐字段,对象引用需要原子更新,而对错对齐的引用不能有这种保证。它们可以跨L1缓存行,这需要两个内存总线周期来设置值。导致撕裂,只看到更新的一部分,这是一个无法调试的问题。
删除所有FieldOffset属性或从参考源复制/粘贴。另一个优势是,如果你的程序以64位模式运行,你会觉得它还能正常工作。
https://stackoverflow.com/questions/43546187
复制相似问题