GetSystemMetrics不能获取多屏物理分辨率,因其仅返回主显示器逻辑尺寸;应使用EnumDisplayMonitors+GetMonitorInfo获取各屏rcMonitor(物理像素),配合GetDpiForMonitor适配多DPI。

GetSystemMetrics 不能直接获取多屏主分辨率
很多人用 GetSystemMetrics(SM_CXSCREEN) 和 GetSystemMetrics(SM_CYSCREEN),结果只拿到主显示器尺寸,且在 DPI 缩放或高分屏下返回的是逻辑像素(非物理像素)。这不是“错”,而是它本来就不为多屏设计——它只反映当前系统 UI 的主显示区域逻辑宽高。
真正需要物理分辨率、支持多屏、适配缩放时,必须用 EnumDisplayMonitors + GetMonitorInfo 或更现代的 GetDpiForMonitor 配合 GetMonitorInfo。
-
GetSystemMetrics在多 DPI 场景下不可靠,比如 125% 缩放时返回值会变小 - 主屏可能不是用户正在使用的屏(例如扩展模式下鼠标在副屏)
- 该 API 已被微软标记为“legacy”,新项目应避免依赖
用 EnumDisplayMonitors + GetMonitorInfo 获取每块屏真实像素
这是 Windows 上获取所有显示器物理分辨率最稳定的方式。关键在于:先枚举句柄,再对每个句柄调用 GetMonitorInfo,从中读取 rcMonitor(整个显示器区域)或 rcWork(扣除任务栏后的可用区域)。
示例核心逻辑:
立即学习“C++免费学习笔记(深入)”;
HMONITOR hMonitor = MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO mi{ sizeof(MONITORINFO) };
if (GetMonitorInfo(hMonitor, &mi)) {
int width = mi.rcMonitor.right - mi.rcMonitor.left;
int height = mi.rcMonitor.bottom - mi.rcMonitor.top;
}- 用
MonitorFromPoint可指定坐标定位某屏;用EnumDisplayMonitors可遍历全部 -
rcMonitor是物理像素范围(DPI 感知开启后仍准确),rcWork扣除了任务栏/停靠工具栏 - 注意结构体初始化必须设
cbSize = sizeof(MONITORINFO),否则GetMonitorInfo返回 false
处理高 DPI 缩放:别硬除缩放比,用 GetDpiForMonitor
Windows 10 1703+ 支持每屏独立 DPI。靠全局缩放比(如 125%)反推物理像素是错的——副屏可能是 100%,主屏却是 150%。
正确做法是:先用 GetDpiForMonitor 获取某屏的 DPI 值(如 144 表示 144 DPI),再结合 GetMonitorInfo 的 rcMonitor 得到真实像素尺寸。DPI 值本身不用于换算分辨率,而是用于字体/渲染适配。
-
GetDpiForMonitor需要shcore.lib链接,且最低支持 Windows 10 - 若需兼容 Win7,改用
GetDpiForSystem(全系统统一 DPI)或SetProcessDpiAwareness提前声明感知级别 - 不要用
(int)(width * scale)这类估算——rcMonitor本身就是物理像素,无需缩放修正
常见错误:CreateDC("DISPLAY") 返回的不是当前屏
有人用 CreateDC("DISPLAY", nullptr, nullptr, nullptr) + GetDeviceCaps 获取 HORZRES/VERTRES,这其实返回的是 GDI 默认设备上下文的设置,不绑定任何具体显示器,且在多屏+DPI 场景下行为未定义。
- 该方式无法区分多屏,
HORZRES常返回主屏逻辑宽度,和GetSystemMetrics类似 - 在启用了“允许 Windows 尝试修复应用模糊”时,结果更不可靠
- 已弃用,微软文档明确建议改用
EnumDisplayMonitors流程
实际开发中,最容易被忽略的是:没有检查 GetMonitorInfo 的返回值,直接访问 rcMonitor 导致未初始化内存读取;还有人把 rcMonitor 和 rcWork 混用,以为后者才是“真实分辨率”,其实前者才是物理边界。










