当前位置: 首页 > news >正文

C# 获取多个显示器信息、分辨率、缩放系数

在很多应用场景中,我们需要获取当前系统的显示器信息,特别是多显示器配置下的各个显示器的分辨率、缩放系数等。例如,在开发图形设计软件、游戏或需要全屏显示的应用时,了解显示器的物理和逻辑分辨率以及缩放系数至关重要。此外,在某些情况下,我们还需要判断多显示器配置是否有效,例如要求所有显示器的缩放系数必须一致。
本文将介绍如何使用 C# 通过 Windows API 来获取显示器的详细信息,计算缩放系数,并判断多显示器配置的有效性。

1. 准备工作

我们将使用以下 Windows API 函数:
  • EnumDisplayMonitors: 枚举所有显示器。
  • GetMonitorInfo: 获取指定显示器的信息,包括设备名称和屏幕矩形。
  • EnumDisplaySettings: 获取指定显示器的显示模式,包括物理分辨率。
同时,我们需要定义一些结构体来存储从 API 获取的数据。

2. 代码实现

以下是完整的 C# 代码,包括 DisplayInfo 类(用于存储显示器信息)、DisplayHelper 类(提供获取显示器信息和判断配置有效性的方法)以及 Program 类(主程序入口)。
csharp
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;namespace dpiTest
{internal class Program{static void Main(string[] args){Console.WriteLine("正在获取显示器信息...\n");// 获取所有显示器信息List<DisplayInfo> myDisplays = DisplayHelper.GetAllDisplaysInfo();// 遍历并打印每个显示器的信息foreach (var display in myDisplays){Console.WriteLine($"==================== 显示器 {display.Index} ====================");Console.WriteLine($"设备名称: {display.DeviceName}");Console.WriteLine($"是否为主显示器: {display.IsPrimary}");Console.WriteLine($"物理分辨率: {display.PhysicalWidth}x{display.PhysicalHeight}");Console.WriteLine($"逻辑分辨率: {display.LogicalWidth}x{display.LogicalHeight}");Console.WriteLine($"缩放系数 (X/Y): {display.ScaleX:F2} / {display.ScaleY:F2}");Console.WriteLine($"缩放百分比 (X/Y): {display.ScalePercentageX:F0}% / {display.ScalePercentageY:F0}%");Console.WriteLine("==============================================================");Console.WriteLine();}// 判断显示配置是否有效bool isConfigurationValid = DisplayHelper.IsDisplayConfigurationValid(myDisplays);// 根据判断结果输出信息if (isConfigurationValid){Console.WriteLine("结论:当前显示配置有效,可以继续执行。");// ... 在这里执行你的业务逻辑 ...}else{Console.WriteLine("结论:当前显示配置无效,请调整显示器缩放设置。");}Console.WriteLine("\n按任意键退出...");Console.ReadKey();}/// <summary>/// 显示器信息类,用于存储单个显示器的详细信息/// </summary>public class DisplayInfo{public int Index { get; set; } // 显示器索引public bool IsPrimary { get; set; } // 是否为主显示器public string DeviceName { get; set; } // 设备名称public int PhysicalWidth { get; set; } // 物理分辨率宽度public int PhysicalHeight { get; set; } // 物理分辨率高度public int LogicalWidth { get; set; } // 逻辑分辨率宽度public int LogicalHeight { get; set; } // 逻辑分辨率高度public float ScaleX { get; set; } // X 方向缩放系数public float ScaleY { get; set; } // Y 方向缩放系数public float ScalePercentageX => ScaleX * 100; // X 方向缩放百分比public float ScalePercentageY => ScaleY * 100; // Y 方向缩放百分比}/// <summary>/// 显示器帮助类,提供获取显示器信息和判断配置有效性的方法/// </summary>public class DisplayHelper{// 为了获取设备名称,需要额外引入几个API[DllImport("user32.dll", CharSet = CharSet.Auto)]private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]private struct MONITORINFOEX{public int cbSize;public RECT rcMonitor;public RECT rcWork;public uint dwFlags;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]public string szDevice;}[StructLayout(LayoutKind.Sequential)]private struct RECT{public int Left;public int Top;public int Right;public int Bottom;}[StructLayout(LayoutKind.Sequential)]private struct DEVMODE{[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]public string dmDeviceName;public short dmSpecVersion;public short dmDriverVersion;public short dmSize;public short dmDriverExtra;public int dmFields;public int dmPositionX;public int dmPositionY;public int dmDisplayOrientation;public int dmDisplayFixedOutput;public short dmColor;public short dmDuplex;public short dmYResolution;public short dmTTOption;public short dmCollate;[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]public string dmFormName;public short dmLogPixels;public int dmBitsPerPel;public int dmPelsWidth;public int dmPelsHeight;public int dmDisplayFlags;public int dmDisplayFrequency;public int dmICMMethod;public int dmICMIntent;public int dmMediaType;public int dmDitherType;public int dmReserved1;public int dmReserved2;public int dmPanningWidth;public int dmPanningHeight;}[DllImport("user32.dll")]private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);[DllImport("shcore.dll")]private static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out uint dpiX, out uint dpiY);[DllImport("user32.dll")]private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsProc lpfnEnum, IntPtr dwData);private delegate bool EnumMonitorsProc(IntPtr hMonitor, IntPtr hdcMonitor, IntPtr lprcMonitorPtr, IntPtr dwData);/// <summary>/// 获取所有显示器的详细信息/// </summary>/// <returns>返回包含所有显示器信息的 DisplayInfo 列表</returns>public static List<DisplayInfo> GetAllDisplaysInfo(){List<DisplayInfo> displays = new List<DisplayInfo>();int index = 1;// 枚举所有显示器EnumMonitorsProc callback = (hMonitor, hdcMonitor, lprcMonitorPtr, dwData) =>{DisplayInfo info = new DisplayInfo();info.Index = index++;// 1. 获取显示器设备名称和逻辑分辨率MONITORINFOEX monitorInfo = new MONITORINFOEX();monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);if (GetMonitorInfo(hMonitor, ref monitorInfo)){info.DeviceName = monitorInfo.szDevice;info.IsPrimary = (monitorInfo.dwFlags & 1) != 0; // 判断是否为主显示器RECT rect = monitorInfo.rcMonitor;info.LogicalWidth = rect.Right - rect.Left;info.LogicalHeight = rect.Bottom - rect.Top;}// 2. 使用设备名称获取物理分辨率if (!string.IsNullOrEmpty(info.DeviceName)){DEVMODE devMode = new DEVMODE();devMode.dmSize = (short)Marshal.SizeOf(devMode);// 使用 -1 (ENUM_CURRENT_SETTINGS) 获取当前生效的分辨率if (EnumDisplaySettings(info.DeviceName, -1, ref devMode)){info.PhysicalWidth = devMode.dmPelsWidth;info.PhysicalHeight = devMode.dmPelsHeight;}}// 3. 计算缩放系数(物理分辨率 / 逻辑分辨率)if (info.PhysicalWidth > 0 && info.LogicalWidth > 0){info.ScaleX = (float)Math.Round((double)info.PhysicalWidth / info.LogicalWidth, 2);info.ScaleY = (float)Math.Round((double)info.PhysicalHeight / info.LogicalHeight, 2);}else{info.ScaleX = 1.0f;info.ScaleY = 1.0f;}displays.Add(info);return true;};EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, IntPtr.Zero);return displays;}/// <summary>/// 根据显示器数量和缩放系数判断配置是否有效。/// 规则:/// 1. 单个显示器 -> 有效 (true)/// 2. 两个显示器且缩放系数一致 -> 有效 (true)/// 3. 两个显示器且缩放系数不一致 -> 无效 (false)/// 4. 超过两个显示器 -> 无效 (false)/// </summary>/// <param name="displaysInfo">由 GetAllDisplaysInfo 方法获取的显示器信息列表</param>/// <returns>配置是否有效的布尔值</returns>public static bool IsDisplayConfigurationValid(List<DisplayInfo> displaysInfo){if (displaysInfo == null){throw new ArgumentNullException(nameof(displaysInfo), "显示器信息列表不能为空。");}// 1. 如果只有一个显示器,直接返回 trueif (displaysInfo.Count == 1){return true;}// 2. 如果有两个显示器else if (displaysInfo.Count == 2){DisplayInfo display1 = displaysInfo[0];DisplayInfo display2 = displaysInfo[1];// 检查两个显示器的缩放系数是否一致(X 和 Y 都要比较)// 使用 Math.Abs 并设置一个容差(例如 0.01)来处理浮点数精度问题bool isScaleXEqual = Math.Abs(display1.ScaleX - display2.ScaleX) < 0.01;bool isScaleYEqual = Math.Abs(display1.ScaleY - display2.ScaleY) < 0.01;return isScaleXEqual && isScaleYEqual;}// 3. 处理超过两个显示器的情况else{return false; // 超过两个显示器,配置无效}}}}
}
 

3. 代码解析

3.1 DisplayInfo 类

这个类用于存储单个显示器的信息,包括:
  • Index: 显示器的索引(从 1 开始)。
  • IsPrimary: 是否为主显示器。
  • DeviceName: 显示器的设备名称(例如 \\.\DISPLAY1)。
  • PhysicalWidth 和 PhysicalHeight: 显示器的物理分辨率。
  • LogicalWidth 和 LogicalHeight: 显示器的逻辑分辨率(缩放后的分辨率)。
  • ScaleX 和 ScaleY: X 和 Y 方向的缩放系数(物理分辨率 / 逻辑分辨率)。
  • ScalePercentageX 和 ScalePercentageY: X 和 Y 方向的缩放百分比(缩放系数 * 100)。

3.2 DisplayHelper 类

这个类提供了两个主要方法:GetAllDisplaysInfo 和 IsDisplayConfigurationValid
3.2.1 GetAllDisplaysInfo 方法
这个方法用于获取所有显示器的详细信息,具体步骤如下:
  1. 使用 EnumDisplayMonitors 枚举所有显示器。
  2. 对于每个显示器,使用 GetMonitorInfo 获取其设备名称和屏幕矩形(逻辑分辨率)。
  3. 使用 EnumDisplaySettings 和设备名称获取该显示器的物理分辨率。
  4. 计算缩放系数(物理分辨率 / 逻辑分辨率),并保留两位小数。
  5. 将所有信息存储在 DisplayInfo 对象中,并添加到列表中返回。
3.2.2 IsDisplayConfigurationValid 方法
这个方法用于判断当前的显示配置是否有效,具体规则如下:
  • 如果只有一个显示器,返回 true
  • 如果有两个显示器,检查它们的缩放系数是否一致(X 和 Y 方向都要比较),如果一致返回 true,否则返回 false
  • 如果超过两个显示器,返回 false
在比较缩放系数时,我们使用了 Math.Abs 并设置了一个容差(0.01),以处理浮点数精度问题。

4. 运行结果

运行上述代码,你将看到类似以下的输出:
plaintext
 
 
正在获取显示器信息...==================== 显示器 1 ====================
设备名称: \\.\DISPLAY1
是否为主显示器: True
物理分辨率: 1920x1080
逻辑分辨率: 1920x1080
缩放系数 (X/Y): 1.00 / 1.00
缩放百分比 (X/Y): 100% / 100%
================================================================================== 显示器 2 ====================
设备名称: \\.\DISPLAY2
是否为主显示器: False
物理分辨率: 2560x1440
逻辑分辨率: 1707x960
缩放系数 (X/Y): 1.50 / 1.50
缩放百分比 (X/Y): 150% / 150%
==============================================================结论:当前显示配置无效,请调整显示器缩放设置。按任意键退出...
 
在这个例子中,有两个显示器,它们的缩放系数不一致(一个是 100%,另一个是 150%),所以显示配置无效。

5. 总结

本文介绍了如何使用 C# 通过 Windows API 来获取显示器的详细信息,包括物理分辨率、逻辑分辨率和缩放系数,并判断多显示器配置的有效性。这对于开发需要适配不同显示配置的应用程序非常有用。
你可以根据自己的需求修改 IsDisplayConfigurationValid 方法中的判断规则,例如允许超过两个显示器但要求所有显示器的缩放系数都一致。
 
http://www.jsqmd.com/news/49467/

相关文章:

  • SELinux笔记-3-Android官方文档 - Hello
  • 2025年国内口碑好的GEO优化实力厂家哪家好,GEO优化AI工具排名/会展服务/短视频矩阵/节目内容策划制作GEO优化品牌排行榜单
  • 哪个医疗器械第三方公司好?资质齐全口碑佳医疗器械公司推荐!
  • 南阳一对一家教辅导机构测评:2026年最新推荐榜单
  • 2025年平口渐缩管厂商权威推荐榜单:坡口渐缩管/螺纹端渐缩管/锻造承插弯头源头厂家精选
  • 如何选择医疗器械第三方机构?口碑 + 实力双考量选择指南!
  • Rust 报错 : may outlive borrowed value - ukyo-
  • 资质齐全!口碑好的医疗器械第三方机构推荐!
  • 2025年重型气缸厂家权威推荐榜单:冶金气缸厂家/铁气缸厂家/QGB气缸厂家源头厂家精选
  • [H3C/华三]FTP与TFTP配置指南
  • vscode密钥从windows连接远程linux
  • 哪家医疗器械第三方机构比较好?专业可靠+安全合规双优清单!
  • AC 自动机小记
  • 用 AI Sheets 解锁图像的力量
  • 支持海外仓一件代发的软件评测!
  • 租房管理系统软件哪个好用?租房管理系统软件排名TO5排行榜
  • 2025年11月降本增效管理咨询公司权威推荐:企业提质增效咨询机构/降本管理咨询公司/十大实力机构深度解析与选择指南
  • 2025 年 11 月包装袋厂家权威推荐榜:无塑纸袋/棉纸袋/格拉辛纸袋,五金袋/服装纸袋/防静电袋,食品包装袋/可降解包装袋/防油袋,精选优质环保材质与创新设计!
  • 2025年人力资源管理咨询公司权威推荐:HR管理咨询,薪酬绩效体系设计,企业绩效考核顾问公司精选,专业实力与客户口碑深度解析
  • jenkins 通过凭据跑流水线!
  • 移动防汛挡板领航者东莞市易禹门业, 质量轻铝合金防汛挡板 + 易拆装防洪防汛挡板,易禹防汛挡板守护万千场景安全
  • 50045_基于微信小程序的民宿预订管理系统
  • AI元人文:全学科的三值纠缠——构建连接自然科学与人文社会的价值动力学框架
  • 2025 年 11 月氢气检测仪厂家权威推荐榜:在线式/固定式/便携式/手持式工业氢气检测仪,精准监测与安全防护的智能之选
  • 2025年乌兰察布悬浮门/升降柱/快速门/直线门/空降门厂商推荐排行榜前十强最新发布
  • 2025年11月贵阳碳纤维加固厂家推荐榜,聚焦加截面/粘钢/截面/桥梁加固推荐技术实力与工程口碑解析
  • 2025 年 11 月战略与供应链管理咨询公司权威推荐:深度解析行业顶尖服务优势与数字化转型实战价值
  • 2025年11月云南小程序开发服务商推荐榜,聚焦昆明app开发/小程序定制开发/定制开发小程序服务商服务口碑!
  • 2025 年 11 月 VOC 检测仪厂家权威推荐榜:在线式 VOC,固定式 VOC,便携式 VOC,手持式 VOC,工业 VOC 检测仪精选品牌与选购指南
  • 东莞易禹门业|专注地铁防淹门、防洪抗洪防淹门、地下通道防淹门,十米水头 + 电动升降式防淹门守护安全