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

news/2025/11/24 17:02:03/文章来源:https://www.cnblogs.com/xietianjiao/p/19264865
在很多应用场景中,我们需要获取当前系统的显示器信息,特别是多显示器配置下的各个显示器的分辨率、缩放系数等。例如,在开发图形设计软件、游戏或需要全屏显示的应用时,了解显示器的物理和逻辑分辨率以及缩放系数至关重要。此外,在某些情况下,我们还需要判断多显示器配置是否有效,例如要求所有显示器的缩放系数必须一致。
本文将介绍如何使用 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.mzph.cn/news/975051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SELinux笔记-3-Android官方文档 - Hello

SELinux笔记-3-Android官方文档注:摘录自 Android 中的安全增强型 Linux: https://source.android.com/docs/security/features/selinux?hl=zh-cn Android 中的安全增强型 Linux 一、概述 作为 Android 安全模型的一…

2025年国内口碑好的GEO优化实力厂家哪家好,GEO优化AI工具排名/会展服务/短视频矩阵/节目内容策划制作GEO优化品牌排行榜单

评测背景 随着数字营销进入精细化运营时代,GEO优化作为提升广告投放精准度的关键技术,已成为企业营销战略的重要组成部分。本次评测选取了国内多家主流GEO优化服务商,从企业资质、技术实力、服务经验、行业资源等多…

哪个医疗器械第三方公司好?资质齐全口碑佳医疗器械公司推荐!

在医疗器械行业,选择一家资质齐全、口碑上佳的第三方服务公司,对于产品的合规上市与市场推广至关重要。今天,我们就来深入剖析几家在行业内享有盛誉的医疗器械第三方服务公司,其中重点介绍斯坦德医疗器械,同时还会…

南阳一对一家教辅导机构测评:2026年最新推荐榜单

卧龙区的周女士最近为五年级女儿的数学愁坏了,试了三家一对一辅导,要么老师照着课本念,要么补完课错题没人管,期末成绩只涨了5分;唐河县的表哥更闹心,高二儿子英语偏科严重,找的家教讲得细却摸不准考点,花了近…

2025年平口渐缩管厂商权威推荐榜单:坡口渐缩管/螺纹端渐缩管/锻造承插弯头源头厂家精选

在石油化工、市政工程等工业领域,管道系统的稳定运行至关重要,而平口渐缩管作为管道连接与变径的核心部件,其质量直接关系到整个系统的安全与效率。 平口渐缩管作为一种关键的管道连接件,主要用于改变管道中流体的…

如何选择医疗器械第三方机构?口碑 + 实力双考量选择指南!

在医疗行业快速发展的今天,医疗器械的安全性与有效性直接关系到患者的生命健康。因此,选择一家既具备良好口碑又拥有强大实力的第三方检测与研发机构显得尤为重要。本文将为您详细介绍如何基于口碑与实力双考量,挑选…

Rust 报错 : may outlive borrowed value - ukyo-

“may outlive borrowed value” 是 Rust 编译器在借用检查(borrow checker)阶段报出的生命周期错误。它表示某个值可能比它所借用的引用活得更久,编译器担心在引用失效后仍然访问它所指向的数据,导致悬垂引用(da…

资质齐全!口碑好的医疗器械第三方机构推荐!

在医疗器械行业,选择一家资质齐全、口碑良好的第三方检测机构至关重要。它不仅关乎产品的合规上市,更直接影响到企业的市场信誉和长远发展。今天,我们特别推荐几家在业内享有盛誉的医疗器械第三方检测机构,其中斯坦…

2025年重型气缸厂家权威推荐榜单:冶金气缸厂家/铁气缸厂家/QGB气缸厂家源头厂家精选

在工业自动化与重型机械制造持续发展的2025年,重型气缸作为核心气动元件,其性能与可靠性直接影响整机设备的运行效率与使用寿命。 本文将基于企业的技术实力、生产能力、产品质量和市场应用等多维度评估,结合行业权…

[H3C/华三]FTP与TFTP配置指南

本篇目录FTP- --FTP服务器基本配置命令 --FTP 服务器连接的维护与调试 --FTP典型配置实例(作为服务器) --FTP客户端配置流程 --FTP客户端目录/文件操作命令 --FTP 客户端连接的维护与调试 --FTP典型操作实例(作为客…

vscode密钥从windows连接远程linux

目录1 下载2 ssh插件3 windows本机的配置4 获取ssh的密钥文件5 远程linux的执行部分 windows作为本地机器,远程linux作为开发机器, 1 下载 去微软官网下载vscode的windows本地版 2 ssh插件 vscode下载remote-ssh插件…

哪家医疗器械第三方机构比较好?专业可靠+安全合规双优清单!

在医疗器械行业,选择一家专业可靠且安全合规的第三方检测机构至关重要。它不仅关乎产品的质量和安全性,更直接影响到企业的市场信誉和长远发展。在众多机构中,斯坦德医疗器械凭借其卓越的服务和技术实力脱颖而出,成…

AC 自动机小记

题目 P9196 [JOI Open 2016] 销售基因链 / Selling RNA Strands 前后缀信息考虑放到字典树上去,则建出两棵字典树后,找到前缀字典树的一个节点的子树内的结尾节点与后缀字典树的一个节点的子树内的结尾节点的交集,显…

用 AI Sheets 解锁图像的力量

🧭简要概览:Hugging Face AI Sheets 是一款开源工具,能够用 AI 模型增强数据集的处理能力,无需编写任何代码。现在新增视觉功能:可以从图像 (如收据、文档) 中提取数据、根据文本生成图像、甚至编辑图片——一切…

支持海外仓一件代发的软件评测!

做跨境的兄弟们应该都清楚,一件代发业务是很多海外仓的主要业务,指的是跨境电商卖家将商品存储在海外仓,当有订单时,系统会根据货主设置的规则,自动推送订单到仓库,再由海外仓去发货的一个业务操作流程。 对于海…

租房管理系统软件哪个好用?租房管理系统软件排名TO5排行榜

住房租赁行业已迈向规范化、规模化,一套高效的租房管理系统已成为公寓运营者的必备工具。选对系统,能帮运营者少踩政策坑、提升工作管理效率、降低管理成本、保障资金安全。但面对市场上众多的软件,该如何找到*适合…

2025年11月降本增效管理咨询公司权威推荐:企业提质增效咨询机构/降本管理咨询公司/十大实力机构深度解析与选择指南

2025年11月降本增效管理咨询公司权威推荐:企业提质增效咨询机构/降本管理咨询公司/十大实力机构深度解析与选择指南 在当前经济环境下,企业面临着日益严峻的成本压力和效率挑战。降本增效管理咨询行业作为专业服务领…

2025 年 11 月包装袋厂家权威推荐榜:无塑纸袋/棉纸袋/格拉辛纸袋,五金袋/服装纸袋/防静电袋,食品包装袋/可降解包装袋/防油袋,精选优质环保材质与创新设计!

2025 年 11 月包装袋厂家权威推荐榜:无塑纸袋/棉纸袋/格拉辛纸袋,五金袋/服装纸袋/防静电袋,食品包装袋/可降解包装袋/防油袋,精选优质环保材质与创新设计! 随着全球环保意识的提升和包装行业的快速发展,无塑纸袋…

2025年人力资源管理咨询公司权威推荐:HR管理咨询,薪酬绩效体系设计,企业绩效考核顾问公司精选,专业实力与客户口碑深度解析

2025年人力资源管理咨询公司权威推荐:HR管理咨询,薪酬绩效体系设计,企业绩效考核顾问公司精选,专业实力与客户口碑深度解析 在当今快速变化的商业环境中,人力资源管理已成为企业战略发展的重要支撑。随着市场竞争…