GetDpiForWindow是Windows 10(1607+)及Win11获取当前窗口DPI缩放因子最直接可靠的方式,返回DPI值需除以96得缩放因子(如144→1.5),但必须提前调用SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2),并在窗口过程中响应WM_DPICHANGED消息以动态更新UI。

Windows平台下用GetDpiForWindow获取当前窗口的实时DPI缩放因子
在Windows 10(1607+)及Windows 11中,GetDpiForWindow是获取窗口所在显示器DPI最直接、最可靠的方式。它返回的是每英寸点数(DPI),不是百分比缩放值;需除以96才能得到缩放因子(如144 DPI → 1.5x)。该函数支持Per-Monitor V2模式,能响应系统缩放变更(比如拖动窗口到不同DPI显示器时)。
关键前提:你的窗口必须启用SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2),否则GetDpiForWindow可能返回主显示器DPI或失败。未设置时调用会返回96(即1.0x),且不报错,极易误判。
- 必须在
WinMain或main入口早期调用SetProcessDpiAwarenessContext,不能晚于创建第一个窗口 -
GetDpiForWindow需要HWND句柄,对无窗口程序(如控制台)不适用;此时应改用GetDpiForMonitor+MonitorFromWindow - 返回值是UINT,不是float;缩放因子建议用
static_cast<double>(dpi) / 96.0</double>计算,避免整数除法截断
跨显示器移动时如何监听DPI变更并更新UI
即使启用了Per-Monitor V2,Windows也不会自动通知你窗口DPI已变——必须自己处理WM_DPICHANGED消息。这个消息在窗口被拖入不同DPI区域、或系统缩放设置更改后触发,携带新DPI和推荐的窗口大小/位置调整矩形。
忽略此消息会导致UI元素模糊、布局错位(比如按钮文字被裁剪),尤其在高分屏间拖拽时非常明显。
立即学习“C++免费学习笔记(深入)”;
- 在窗口过程函数中捕获
WM_DPICHANGED,不要只依赖首次创建时查一次DPI - 消息参数
wParam是新DPI值,lParam指向RECT*,给出建议的窗口尺寸重设范围;通常需调用SetWindowPos按比例缩放窗口客户区 - 若使用Direct2D或GDI绘图,需同步更新渲染目标DPI(如
ID2D1Factory::SetDpi)和字体大小
Linux/X11下通过_NET_WORKAREA和XRandR间接估算缩放因子
Linux没有统一的“系统DPI缩放因子”API。GNOME/KDE等桌面环境各自维护缩放设置(如GNOME的gsettings get org.gnome.desktop.interface scaling-factor),但这是用户级偏好,不一定反映真实物理DPI。更可靠的方式是结合屏幕物理尺寸与逻辑分辨率推算。
实际开发中,多数C++ GUI库(Qt、GTK)会封装这些细节;纯X11应用需手动协调多个来源。
- 用
XRRGetScreenResources获取当前输出(output)的物理尺寸(mm)和分辨率(px),再算DPI:sqrt(w_px² + h_px²) / sqrt(w_mm² + h_mm²) * 25.4 - 检查
_NET_WORKAREA原子值可辅助判断是否启用了fractional scaling(如125%),但该值无标准定义,KDE/GNOME行为不一致 - 注意Wayland会话下X11接口不可靠,应优先使用
xdg-output协议(需Wayland客户端库支持)
macOS上用NSScreen的backingScaleFactor获取缩放因子
macOS中,每个NSScreen对象的backingScaleFactor属性直接返回当前缩放因子(1.0、2.0,或非整数如1.5、1.25),无需换算。该值在Retina屏开启/关闭、系统缩放设置变更时实时更新,且对Metal/OpenGL/Cocoa绘图天然适配。
难点在于C++代码需桥接Objective-C运行时,不能直接在纯C++文件里调用。
- 必须将文件后缀改为
.mm启用Objective-C++,并在头文件中#import <AppKit/NSScreen.h> - 获取主屏缩放:
[NSScreen mainScreen].backingScaleFactor;遍历所有屏:[NSScreen screens]数组 - 监听变化需注册
NSApplicationDidChangeScreenParametersNotification通知,而非轮询 - 注意:
backingScaleFactor是绘制缩放因子,UI布局仍需按逻辑像素设计(如窗口尺寸用points而非pixels)
实际项目中最容易漏掉的是Windows下的WM_DPICHANGED响应和Linux下对Wayland会话的fallback处理——这两个点一旦忽略,多显示器场景下的体验会直接降级。


















