【发布时间】:2019-12-13 01:38:11
【问题描述】:
我有以下 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;
}
}
}
【问题讨论】:
-
@canton7 我已经尝试过该代码,但我得到了与他们的
dmFormName完全相同的错误 -
将您的代码与pinvoke.net/default.aspx/Structures/DEVMODE.html 进行比较,我发现
CharSet不同。会不会是这个原因? -
你也没有设置
dmSize——我怀疑这是原因,但你需要在某个时候设置它,所以只需检查它是否不是原因。请参阅相同的 pinvoke 链接了解如何设置它。 -
@canton7 据我所知不是。 ANSI 字符集没有任何区别。我在搜索我的问题的解决方案时遇到了他们的代码,据我所知,主要区别在于我使用了类型名称别名并重构了结构,而不是显式定义每个字段的位置。
-
@DavidHeffernan 我认为他们需要与 C++ 联合进行交互? IE。其中
PrinterOnlyFields和DisplayOnlyFields都必须在字段 44
标签: c# c++ .net winapi interop