GetDpiForWindow是Windows 10 1703+及Win11获取当前窗口实际DPI最直接方式,返回整数DPI值(如144),除以96得缩放因子(1.5);需窗口已创建、句柄有效,且进程启用DPI感知,否则恒返96,并须响应WM_DPICHANGED动态更新。

Windows平台用GetDpiForWindow获取当前窗口DPI缩放因子
在Windows 10 1703+和Windows 11上,GetDpiForWindow是获取窗口实际DPI最直接的方式。它返回的是每英寸点数(DPI),不是百分比缩放值;需除以96才能得到缩放因子(如144 DPI → 1.5x)。注意:该函数要求目标窗口已创建且句柄有效,不能在WM_CREATE之前调用。
- 必须链接
User32.lib,头文件包含<winuser.h> - 若传入
NULL或无效HWND,返回USER_DEFAULT_DPI(96),不是错误码 - 对多显示器混合缩放场景,每个窗口可能返回不同DPI,必须按窗口单独查
- 示例:
int dpi = GetDpiForWindow(hwnd);<br>float scale = static_cast<float>(dpi) / 96.0f;
监听DPI变更用WM_DPICHANGED而非轮询
用户拖动窗口跨显示器、手动调整系统缩放设置时,系统会向窗口发送WM_DPICHANGED消息。这是唯一可靠的实时响应机制——不要用定时器轮询GetDpiForWindow,既耗资源又错过瞬时变更。
- 消息参数:
wParam是新DPI值,lParam低32位为新窗口矩形(需据此调整窗口大小) - 收到后必须调用
SetWindowPos重置窗口尺寸,否则界面会被裁剪或留白 - 仅适用于已启用DPI感知的进程,否则系统会自动缩放整个窗口(模糊),根本不会发此消息
- 在
WndProc中处理:case WM_DPICHANGED:<br> SetWindowPos(hwnd, nullptr,<br> LOWORD(lParam), HIWORD(lParam),<br GET_X_LPARAM(lParam) - LOWORD(lParam),<br GET_Y_LPARAM(lParam) - HIWORD(lParam),<br SWP_NOZORDER | SWP_NOACTIVATE);<br> break;
声明DPI感知模式决定能否拿到真实DPI
即使代码调用了GetDpiForWindow,若进程未声明DPI感知级别,Windows会强制以96 DPI模拟运行,并屏蔽真实DPI通知。必须通过manifest或API显式声明。
- 推荐方式:在manifest中添加
<dpiAware>true/PM</dpiAware>(支持Per-Monitor v2) - 运行时调用
SetProcessDpiAwarenessContext(Windows 10 1703+)比旧版SetProcessDpiAwareness更可靠 - 若只设
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE,跨显示器时仍会使用主屏DPI,不是真正Per-Monitor - 未声明感知的程序,
GetDpiForWindow始终返回96,WM_DPICHANGED永不触发
Linux/macOS没有等价的系统级DPI因子概念
Linux下DPI缩放由X11/Wayland合成器(如GNOME/KDE)和应用工具包(Qt/GTK)分层控制,C++程序无法像Windows那样直接读取“系统缩放因子”。macOS则用[NSScreen backingScaleFactor]获取逻辑像素比,但仅对本屏幕有效,且需Cocoa上下文。
立即学习“C++免费学习笔记(深入)”;
- Linux建议:读取环境变量
GDK_SCALE(GTK)或QT_SCALE_FACTOR(Qt),但非标准,且不反映Wayland动态缩放 - macOS必须用Objective-C++桥接,纯C++无法调用
NSScreenAPI - 跨平台库(如SDL2、glfw)提供
glfwGetMonitorContentScale,但返回的是物理像素/逻辑像素比,与Windows的DPI因子数值一致,可统一处理 - 关键区别:Windows的DPI因子是系统策略强干预结果;Linux/macOS更多是UI工具包协商结果,C++层无统一抽象
真正麻烦的是Per-Monitor v2的兼容性边界——比如窗口从125%屏拖到200%屏时,WM_DPICHANGED可能在窗口尚未完成重绘时就到达,此时GetClientRect返回的尺寸仍是旧值,直接按新DPI缩放控件会导致布局错乱。这种竞态必须靠双缓冲重绘+延迟布局更新来缓解,不是简单改个缩放因子就能解决的。


















