Windows用GetDpiForWindow获取实时DPI(需有效HWND,返回值÷96得缩放比);Linux/X11读_NET_SCALE_FACTOR或monitorScale(非实时,无fallback);macOS用NSScreen.backingScaleFactor并监听通知;各平台调用线程与时机要求不同。

Windows 上用 GetDpiForWindow 获取当前窗口的实时 DPI 缩放比例
Windows 10 1703+ 支持 per-monitor DPI,GetDpiForWindow 是最直接、最可靠的实时获取方式——它不依赖全局系统设置,也不受进程 DPI 感知模式影响(只要窗口已创建且句柄有效)。关键点在于:必须传入一个**已创建且可见的窗口句柄**(HWND),不能用 NULL 或未初始化的句柄。
-
GetDpiForWindow返回的是每英寸点数(DPI),需除以 96 才得缩放比例(如 144 → 1.5x) - 若返回值为 0,说明窗口无效或尚未完成 DPI 初始化(常见于
WM_CREATE早期调用) - 该函数在多显示器场景下自动返回对应显示器的 DPI,无需手动查询显示器边界
- 务必确保程序 manifest 中声明了
highVersionAware=true且设置了dpiAwareness(推荐perMonitorV2),否则可能始终返回 96
Linux(X11)下读取 _NET_SCALE_FACTOR 和 monitorScale 的实际值
X11 没有统一标准 API,主流桌面环境靠扩展属性传递缩放信息。_NET_SCALE_FACTOR(由 GNOME/Pop!_OS 等设置)和 monitorScale(KDE Plasma 使用)是两个最常被读取的属性,但它们不是实时更新的——只在显示配置变更(如插拔显示器、切换缩放)后由 WM 设置一次。
- 用
XGetWindowProperty查询根窗口的_NET_SCALE_FACTOR,类型为XA_CARDINAL,值为浮点倍数(如 2.0 表示 200%) - KDE 用户需额外检查
monitorScale属性(类型同为XA_CARDINAL),优先级高于_NET_SCALE_FACTOR - 注意:Wayland 下这些 X11 属性不可用;Qt/GTK 应用应改用各自平台抽象层(如
QScreen::devicePixelRatio()) - 没有 fallback 机制——若属性不存在,只能假设为 1.0,无法探测是否“未设置”还是“明确设为 1”
macOS 中通过 NSScreen 获取主屏与活跃屏的 backingScaleFactor
macOS 的缩放是 per-screen 且硬件绑定的,backingScaleFactor 直接反映当前屏幕的像素密度比(1.0=标准分辨率,2.0=Retail Retina,其他值如 1.5/1.25 来自“缩放显示”设置)。它会随用户在「系统设置 > 显示器 > 缩放」中切换而实时变化,但需要监听通知才能捕获变更。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
[NSScreen mainScreen].backingScaleFactor获取主屏值;[NSScreen orderedScreens]遍历可得所有屏的当前值 - 监听
NSApplicationDidChangeScreenParametersNotification可响应缩放变更(注意:该通知不保证在所有 macOS 版本中触发,12+ 更可靠) - 不要用
[NSScreen frame]或visibleFrame推算缩放——它们返回的是逻辑坐标,已应用缩放,无法反推原始因子 - AppKit 应用默认能正确响应;C++ CLI 或纯 C 接口需通过 Objective-C++ 桥接调用
跨平台封装时容易忽略的 DPI 感知状态与线程安全问题
很多开发者把 DPI 查询封装成单例工具函数,却忘了不同平台对调用时机和线程的要求差异极大。例如 Windows 的 GetDpiForWindow 必须在 UI 线程调用(否则可能返回错误值),而 macOS 的 NSScreen 方法虽允许后台线程读取,但通知监听必须在主线程注册。
立即学习“C++免费学习笔记(深入)”;
- Windows:确保
GetDpiForWindow在窗口所属线程(通常是主线程)执行;跨线程调用可能返回 0 或旧值 - Linux/X11:
XGetWindowProperty是线程安全的,但需确保Display*连接未被其他线程关闭 - macOS:
backingScaleFactor读取本身线程安全,但通知回调必须在主线程——若用 GCD,需显式 dispatch 到dispatch_get_main_queue() - 缓存策略要谨慎:不能简单“查一次存全局”,因为用户可能动态切换显示器缩放,必须配合平台特定的通知机制刷新
真正麻烦的不是怎么取值,而是确认这个值此刻是否代表真实渲染上下文——窗口还没显示、屏幕刚热插拔、Wayland session 切换中……这些边界情况没有银弹,只能按平台逐个防御。

















