未正确对齐的字段
Incorrectly Aligned Field
我有以下 C++ 可以使用 EnumDisplaySettings
WinAPI 函数获取有关特定监视器的信息。
#include <iostream>
#include <Windows.h>
int main()
{
DEVMODE dm;
dm.dmSize = sizeof dm;
EnumDisplaySettings(L"\\.\DISPLAY1", ENUM_CURRENT_SETTINGS, &dm);
std::wcout << "Name: " << dm.dmDeviceName << std::endl;
std::wcout << "Width: " << dm.dmPelsWidth << std::endl;
std::wcout << "Height: " << dm.dmPelsHeight << std::endl;
}
我正在尝试使用 C# 中的 EnumDisplaySettings
函数。
为此,我重新创建了 DEVMODEW 作为 C# 结构并将其传递到方法中。
static void Main()
{
DeviceModeStruct deviceMode = new DeviceModeStruct();
deviceMode.dmSize = (ushort)Marshal.SizeOf(deviceMode);
bool successfullyGotScale = EnumDisplaySettings("\\.\DISPLAY1",
ENUM_CURRENT_SETTINGS,
ref deviceMode);
if (successfullyGotScale)
{
Console.WriteLine($@"Name: {deviceMode.dmDeviceName}");
Console.WriteLine($@"Width: {deviceMode.dmPelsWidth}");
Console.WriteLine($@"Height: {deviceMode.dmPelsHeight}");
}
}
问题是,当我 运行 代码时,出现以下异常。
Unhandled Exception: System.TypeLoadException: Could not load type 'DeviceModeStruct'
from assembly 'DevModeSo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
because it contains an object field at offset 70 that is incorrectly aligned or
overlapped by a non-object field.
at DevModeSo.Program.Main()
据我所知,问题与
有关
[FieldOffset(70), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmFormName;
和 this answer 另一个类似的 Stack Overflow 问题似乎表明我可以拆分字符串来解决问题。
但是,当我尝试这样做以便代码使值与 "DWORDS" 对齐时,我遇到了同样的错误。
[FieldOffset(70)]
public char dmFormName1;
[FieldOffset(71)]
public char dmFormName2;
[FieldOffset(72), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
public string dmFormName3;
如何解决这个问题,同时仍然满足 DEVMODEW 定义的相同数据结构?
完整的 C# 代码
using System;
using System.Runtime.InteropServices;
namespace DevModeSo
{
class Program
{
private const int ENUM_CURRENT_SETTINGS = -1;
static void Main()
{
DeviceModeStruct deviceMode = new DeviceModeStruct();
deviceMode.dmSize = (ushort)Marshal.SizeOf(deviceMode);
bool successfullyGotScale = EnumDisplaySettings("\\.\DISPLAY1",
ENUM_CURRENT_SETTINGS,
ref deviceMode);
if (successfullyGotScale)
{
Console.WriteLine($@"Name: {deviceMode.dmDeviceName}");
Console.WriteLine($@"Width: {deviceMode.dmPelsWidth}");
Console.WriteLine($@"Height: {deviceMode.dmPelsHeight}");
}
}
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool EnumDisplaySettings(string deviceName,
int modeNum,
ref DeviceModeStruct deviceMode);
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
struct DeviceModeStruct
{
private const int STRING_SIZE = 32;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmDeviceName;
[FieldOffset(32)] public ushort dmSpecVersion;
[FieldOffset(34)] public ushort dmDriverVersion;
[FieldOffset(36)] public ushort dmSize;
[FieldOffset(38)] public ushort dmDriverExtra;
[FieldOffset(40)] public uint dmFields;
[FieldOffset(44)] public PrinterOnlyFields printerMode;
[FieldOffset(44)] public DisplayOnlyFields displayMode;
[FieldOffset(60)] public short dmColor;
[FieldOffset(62)] public short dmDuplex;
[FieldOffset(64)] public short dmYResolution;
[FieldOffset(66)] public short dmTTOption;
[FieldOffset(68)] public short dmCollate;
[FieldOffset(70), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmFormName;
[FieldOffset(102)] public ushort dmLogPixels;
[FieldOffset(104)] public uint dmBitsPerPel;
[FieldOffset(108)] public uint dmPelsWidth;
[FieldOffset(112)] public uint dmPelsHeight;
[FieldOffset(116)] public uint dmDisplayFlags;
[FieldOffset(116)] public uint dmNup;
[FieldOffset(120)] public uint dmDisplayFrequency;
[FieldOffset(124)] public uint dmICMMethod;
[FieldOffset(128)] public uint dmICMIntent;
[FieldOffset(132)] public uint dmMediaType;
[FieldOffset(136)] public uint dmDitherType;
[FieldOffset(140)] public uint dmReserved1;
[FieldOffset(144)] public uint dmReserved2;
[FieldOffset(148)] public uint dmPanningWidth;
[FieldOffset(152)] public uint dmPanningHeight;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct PrinterOnlyFields
{
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct Point
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct DisplayOnlyFields
{
public Point dmPosition;
public uint dmDisplayOrientation;
public uint dmDisplayFixedOutput;
}
}
}
您应该删除所有 FieldOffset
属性并在 DeviceModeStruct
结构之外实施联合。像这样:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
struct DeviceModeUnion
{
[FieldOffset(0)]
PrinterOnlyFields Printer;
[FieldOffset(0)]
Point Position;
[FieldOffset(0)]
DisplayOnlyFields Display;
}
....
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct DeviceModeStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public ushort dmSpecVersion;
public ushort dmDriverVersion;
public ushort dmSize;
public ushort dmDriverExtra;
public uint dmFields;
public DeviceModeUnion union;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public ushort dmLogPixels;
public uint dmBitsPerPel;
public uint dmPelsWidth;
public uint dmPelsHeight;
public uint dmDisplayFlags;
public uint dmDisplayFrequency;
public uint dmICMMethod;
public uint dmICMIntent;
public uint dmMediaType;
public uint dmDitherType;
public uint dmReserved1;
public uint dmReserved2;
public uint dmPanningWidth;
public uint dmPanningHeight;
}
我没有根据 documentation 仔细检查 DeviceModeStruct
,但我相信你可以做到。但是,与 C++ 头文件中定义的结构的大小相比,我可以确认此结构定义至少具有正确的大小。
我有以下 C++ 可以使用 EnumDisplaySettings
WinAPI 函数获取有关特定监视器的信息。
#include <iostream>
#include <Windows.h>
int main()
{
DEVMODE dm;
dm.dmSize = sizeof dm;
EnumDisplaySettings(L"\\.\DISPLAY1", ENUM_CURRENT_SETTINGS, &dm);
std::wcout << "Name: " << dm.dmDeviceName << std::endl;
std::wcout << "Width: " << dm.dmPelsWidth << std::endl;
std::wcout << "Height: " << dm.dmPelsHeight << std::endl;
}
我正在尝试使用 C# 中的 EnumDisplaySettings
函数。
为此,我重新创建了 DEVMODEW 作为 C# 结构并将其传递到方法中。
static void Main()
{
DeviceModeStruct deviceMode = new DeviceModeStruct();
deviceMode.dmSize = (ushort)Marshal.SizeOf(deviceMode);
bool successfullyGotScale = EnumDisplaySettings("\\.\DISPLAY1",
ENUM_CURRENT_SETTINGS,
ref deviceMode);
if (successfullyGotScale)
{
Console.WriteLine($@"Name: {deviceMode.dmDeviceName}");
Console.WriteLine($@"Width: {deviceMode.dmPelsWidth}");
Console.WriteLine($@"Height: {deviceMode.dmPelsHeight}");
}
}
问题是,当我 运行 代码时,出现以下异常。
Unhandled Exception: System.TypeLoadException: Could not load type 'DeviceModeStruct'
from assembly 'DevModeSo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
because it contains an object field at offset 70 that is incorrectly aligned or
overlapped by a non-object field.
at DevModeSo.Program.Main()
据我所知,问题与
有关[FieldOffset(70), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmFormName;
和 this answer 另一个类似的 Stack Overflow 问题似乎表明我可以拆分字符串来解决问题。
但是,当我尝试这样做以便代码使值与 "DWORDS" 对齐时,我遇到了同样的错误。
[FieldOffset(70)]
public char dmFormName1;
[FieldOffset(71)]
public char dmFormName2;
[FieldOffset(72), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
public string dmFormName3;
如何解决这个问题,同时仍然满足 DEVMODEW 定义的相同数据结构?
完整的 C# 代码
using System;
using System.Runtime.InteropServices;
namespace DevModeSo
{
class Program
{
private const int ENUM_CURRENT_SETTINGS = -1;
static void Main()
{
DeviceModeStruct deviceMode = new DeviceModeStruct();
deviceMode.dmSize = (ushort)Marshal.SizeOf(deviceMode);
bool successfullyGotScale = EnumDisplaySettings("\\.\DISPLAY1",
ENUM_CURRENT_SETTINGS,
ref deviceMode);
if (successfullyGotScale)
{
Console.WriteLine($@"Name: {deviceMode.dmDeviceName}");
Console.WriteLine($@"Width: {deviceMode.dmPelsWidth}");
Console.WriteLine($@"Height: {deviceMode.dmPelsHeight}");
}
}
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool EnumDisplaySettings(string deviceName,
int modeNum,
ref DeviceModeStruct deviceMode);
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
struct DeviceModeStruct
{
private const int STRING_SIZE = 32;
[FieldOffset(0), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmDeviceName;
[FieldOffset(32)] public ushort dmSpecVersion;
[FieldOffset(34)] public ushort dmDriverVersion;
[FieldOffset(36)] public ushort dmSize;
[FieldOffset(38)] public ushort dmDriverExtra;
[FieldOffset(40)] public uint dmFields;
[FieldOffset(44)] public PrinterOnlyFields printerMode;
[FieldOffset(44)] public DisplayOnlyFields displayMode;
[FieldOffset(60)] public short dmColor;
[FieldOffset(62)] public short dmDuplex;
[FieldOffset(64)] public short dmYResolution;
[FieldOffset(66)] public short dmTTOption;
[FieldOffset(68)] public short dmCollate;
[FieldOffset(70), MarshalAs(UnmanagedType.ByValTStr, SizeConst = STRING_SIZE)]
public string dmFormName;
[FieldOffset(102)] public ushort dmLogPixels;
[FieldOffset(104)] public uint dmBitsPerPel;
[FieldOffset(108)] public uint dmPelsWidth;
[FieldOffset(112)] public uint dmPelsHeight;
[FieldOffset(116)] public uint dmDisplayFlags;
[FieldOffset(116)] public uint dmNup;
[FieldOffset(120)] public uint dmDisplayFrequency;
[FieldOffset(124)] public uint dmICMMethod;
[FieldOffset(128)] public uint dmICMIntent;
[FieldOffset(132)] public uint dmMediaType;
[FieldOffset(136)] public uint dmDitherType;
[FieldOffset(140)] public uint dmReserved1;
[FieldOffset(144)] public uint dmReserved2;
[FieldOffset(148)] public uint dmPanningWidth;
[FieldOffset(152)] public uint dmPanningHeight;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct PrinterOnlyFields
{
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct Point
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct DisplayOnlyFields
{
public Point dmPosition;
public uint dmDisplayOrientation;
public uint dmDisplayFixedOutput;
}
}
}
您应该删除所有 FieldOffset
属性并在 DeviceModeStruct
结构之外实施联合。像这样:
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto)]
struct DeviceModeUnion
{
[FieldOffset(0)]
PrinterOnlyFields Printer;
[FieldOffset(0)]
Point Position;
[FieldOffset(0)]
DisplayOnlyFields Display;
}
....
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct DeviceModeStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public ushort dmSpecVersion;
public ushort dmDriverVersion;
public ushort dmSize;
public ushort dmDriverExtra;
public uint dmFields;
public DeviceModeUnion union;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public ushort dmLogPixels;
public uint dmBitsPerPel;
public uint dmPelsWidth;
public uint dmPelsHeight;
public uint dmDisplayFlags;
public uint dmDisplayFrequency;
public uint dmICMMethod;
public uint dmICMIntent;
public uint dmMediaType;
public uint dmDitherType;
public uint dmReserved1;
public uint dmReserved2;
public uint dmPanningWidth;
public uint dmPanningHeight;
}
我没有根据 documentation 仔细检查 DeviceModeStruct
,但我相信你可以做到。但是,与 C++ 头文件中定义的结构的大小相比,我可以确认此结构定义至少具有正确的大小。