本文将和大家介绍在 WPF 里面调用 Win32 的 DisplayConfigGetDeviceInfo 获取显示器名的方法
界面代码十分简单,就放一个 TextBlock 用于显示获取到的显示器信息
<Grid> <TextBlock x:Name="MonitorNameTextBlock" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock> </Grid>获取显示器名的步骤如下:
- 通过 GetDisplayConfigBufferSizes 和 QueryDisplayConfig 获取基础信息
- 通过 DisplayConfigGetDeviceInfo 配合 DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME 获取显示器名
核心调用代码如下,虽然代码看起来多,但大部分都是 Win32 的类型定义
public partial class MainWindow : Window{ public MainWindow() { InitializeComponent();
var displayInfo = GetDisplayInfoList().FirstOrDefault(); MonitorNameTextBlock.Text = displayInfo?.Name ?? "Null"; }
private static IReadOnlyList<DisplayInfo> GetDisplayInfoList() { var (pathInfos, modeInfos) = GetActivePathInfoAndModeInfo();
var displayInfoList = new List<DisplayInfo>();
foreach (var displayConfigPathInfo in pathInfos) { if (!displayConfigPathInfo.flags.HasFlag(DISPLAYCONFIG_PATH_INFOFlags.DISPLAYCONFIG_PATH_ACTIVE)) { continue; }
var sourceMode = modeInfos[displayConfigPathInfo.sourceInfo.modeInfoIdx].sourceMode; var targetMode = modeInfos[displayConfigPathInfo.targetInfo.modeInfoIdx].targetMode; _ = targetMode;
var displayInfo = new DisplayInfo { Width = sourceMode.width, Height = sourceMode.height, Left = sourceMode.position.x, Top = sourceMode.position.y, Name = GetName(displayConfigPathInfo.targetInfo), }; displayInfoList.Add(displayInfo); }
return displayInfoList; }
private static unsafe string? GetName(DISPLAYCONFIG_PATH_TARGET_INFO targetInfo) { string? name = null;
DISPLAYCONFIG_TARGET_DEVICE_NAME displayConfigTargetDeviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME(); displayConfigTargetDeviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; displayConfigTargetDeviceName.header.adapterId = targetInfo.adapterId; displayConfigTargetDeviceName.header.id = targetInfo.id; displayConfigTargetDeviceName.header.size = (uint)sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
var result = DisplayConfigGetDeviceInfo((IntPtr)(&displayConfigTargetDeviceName));
if (result == ERROR_SUCCESS) { name = new string((char*)(&(displayConfigTargetDeviceName.monitorFriendlyDeviceName))); }
return name; }
private const int ERROR_SUCCESS = 0;
/// <summary>The data area passed to a system call is too small.</summary> private const int ERROR_INSUFFICIENT_BUFFER = 122; // 0x0000007A
private static (DISPLAYCONFIG_PATH_INFO[] pathInfos, DISPLAYCONFIG_MODE_INFO[] modeInfos) GetActivePathInfoAndModeInfo() { DISPLAYCONFIG_PATH_INFO[] pathInfos; DISPLAYCONFIG_MODE_INFO[] modeInfos; while (true) { var result = GetDisplayConfigBufferSizes(QueryDisplayConfigFlags.QDC_ONLY_ACTIVE_PATHS, out var pathArrayLength, out var modeArrayLength); if (result != ERROR_SUCCESS) { throw new Win32Exception((int)result); }
pathInfos = new DISPLAYCONFIG_PATH_INFO[pathArrayLength]; modeInfos = new DISPLAYCONFIG_MODE_INFO[modeArrayLength]; result = QueryDisplayConfig(QueryDisplayConfigFlags.QDC_ONLY_ACTIVE_PATHS, ref pathArrayLength, pathInfos, ref modeArrayLength, modeInfos, IntPtr.Zero); if (result == ERROR_SUCCESS) { break; } else if (result == ERROR_INSUFFICIENT_BUFFER) { continue; } else { throw new Win32Exception((int)result); } }
return (pathInfos, modeInfos); }
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetDisplayConfigBufferSizes", ExactSpelling = true, SetLastError = true)] private static extern uint GetDisplayConfigBufferSizes([In] QueryDisplayConfigFlags flags, [Out] out int numPathArrayElements, [Out] out int numModeInfoArrayElements);
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "QueryDisplayConfig", ExactSpelling = true, SetLastError = true)] private static extern uint QueryDisplayConfig([In] QueryDisplayConfigFlags flags, [In] [Out] ref int numPathArrayElements, [Out] DISPLAYCONFIG_PATH_INFO[] pathArray, [In] [Out] ref int numModeInfoArrayElements, [Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, [In] IntPtr currentTopologyId);
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DisplayConfigGetDeviceInfo", ExactSpelling = true, SetLastError = true)] public static extern uint DisplayConfigGetDeviceInfo([In] IntPtr requestPacket);
[Flags] private enum QueryDisplayConfigFlags : uint { QDC_ALL_PATHS = 0x00000001, QDC_ONLY_ACTIVE_PATHS = 0x00000002, QDC_DATABASE_CURRENT = 0x00000004, QDC_VIRTUAL_MODE_AWARE = 0x00000010, QDC_INCLUDE_HMD = 0x00000020, QDC_VIRTUAL_REFRESH_RATE_AWARE = 0x00000040, }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] private struct DISPLAYCONFIG_PATH_INFO { public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; public DISPLAYCONFIG_PATH_INFOFlags flags; }
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_MODE_INFO { [FieldOffset(0)] public DISPLAYCONFIG_MODE_INFO_TYPE infoType; [FieldOffset(4)] public uint id; [FieldOffset(8)] public long adapterId; [FieldOffset(16)] public DISPLAYCONFIG_TARGET_MODE targetMode; [FieldOffset(16)] public DISPLAYCONFIG_SOURCE_MODE sourceMode; }
private enum DISPLAYCONFIG_MODE_INFO_TYPE { DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2, }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] private struct DISPLAYCONFIG_PATH_SOURCE_INFO { public long adapterId; public uint id; public uint modeInfoIdx; public uint statusFlags; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_PATH_TARGET_INFO { public long adapterId; public uint id; public uint modeInfoIdx; public int outputTechnology; public int rotation; public int scaling; public int refreshRate; public int scanLineOrdering; public bool targetAvailable; public int statusFlags; }
[Flags] private enum DISPLAYCONFIG_PATH_INFOFlags : uint { DISPLAYCONFIG_PATH_ACTIVE = 0x00000001, DISPLAYCONFIG_PATH_SUPPORT_VIRTUAL_MODE = 0x00000008, DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE = 0x00000010, }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_TARGET_MODE { public DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_SOURCE_MODE { public int width; public int height; public DISPLAYCONFIG_PIXELFORMAT pixelFormat; public POINTL position; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { public ulong pixelRate; public ulong hSyncFreq; public ulong vSyncFreq; public DISPLAYCONFIG_2DREGION activeSize; public DISPLAYCONFIG_2DREGION totalSize; public uint videoStandard; public uint scanLineOrdering; }
private enum DISPLAYCONFIG_PIXELFORMAT { DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5, }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct POINTL { public int x; public int y; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_2DREGION { public uint cx; public uint cy; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] private struct DISPLAYCONFIG_DEVICE_INFO_HEADER { public DISPLAYCONFIG_DEVICE_INFO_TYPE type; public uint size; public long adapterId; public uint id; }
private enum DISPLAYCONFIG_DEVICE_INFO_TYPE { DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11, DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION, DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION, }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 64 * sizeof(char))] private struct ByValStringStructForSize64 { }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = 128 * sizeof(char))] private struct ByValStringStructForSize128 { }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DISPLAYCONFIG_TARGET_DEVICE_NAME { public DISPLAYCONFIG_DEVICE_INFO_HEADER header; public uint flags; public int outputTechnology; public ushort edidManufactureId; public ushort edidProductCodeId; public uint connectorInstance; public ByValStringStructForSize64 monitorFriendlyDeviceName; public ByValStringStructForSize128 monitorDevicePath; }}
record DisplayInfo{ public int Width { get; init; }
public int Height { get; init; }
public int Left { get; init; }
public int Top { get; init; }
public string? Name { get; init; }}尝试运行代码,可见在界面显示出首个显示器名。如我的是戴尔的显示器,界面内容如下

本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git initgit remote add origin https://gitee.com/lindexi/lindexi_gd.gitgit pull origin 12919d419d7988a99a6a72936ab0d987fed14be7以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
git remote remove origingit remote add origin https://github.com/lindexi/lindexi_gd.gitgit pull origin 12919d419d7988a99a6a72936ab0d987fed14be7获取代码之后,进入 WPFDemo/RekafagigerjuNebecocaici 文件夹,即可获取到源代码
更多技术博客,请参阅 博客导航
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。