windows首选getdpiforwindow(需正确声明dpi感知),linux无统一api只能按环境适配,macos用[nsscreen backingscalefactor]最简洁。

Windows平台用GetDpiForWindow最可靠
Windows 10 1703+ 系统下,GetDpiForWindow 是获取窗口所在显示器 DPI 缩放比例的首选方式。它返回的是每英寸点数(DPI),缩放比例 = DPI / 96。比如返回 144,说明缩放为 150%。
关键点:必须传入一个有效的窗口句柄(HWND),不能传 NULL 或无效句柄,否则返回 96(默认值),且不报错——这是最常见的误用坑。
- 若在 UI 线程中调用,直接用当前窗口句柄:
GetDpiForWindow(hWnd) - 若无窗口(如控制台程序),可用
GetDpiForSystem()获取系统主显示器 DPI,但它忽略多屏不同缩放场景 - 不要用已废弃的
GetDeviceCaps(hdc, LOGPIXELSX),它在高 DPI 模式下可能返回错误值,除非你显式启用了 DPI 感知并正确设置了 manifest
需要提前声明 DPI 感知级别
即使调用 GetDpiForWindow,如果进程未声明 DPI 感知,Windows 会强制以 96 DPI 虚拟化你的窗口,导致返回值恒为 96,而实际显示被拉伸或模糊。
必须在 manifest 文件中设置:<dpiaware>true/PM</dpiaware>(推荐 true/PM,即 Per-Monitor v2),或在代码中调用 SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)(Windows 10 1703+)。
- 仅调用
SetProcessDpiAwareness(旧 API)不够,它不支持多屏混合缩放 - manifest 和 API 设置需同时到位:manifest 声明能力,API 在运行时激活
- 控制台程序默认无 manifest,必须手动添加或改用 GUI 子系统(
/subsystem:windows)才能生效
Linux/X11 下没有统一 DPI 接口
X11 本身不提供标准 API 获取屏幕缩放比例,实际依赖桌面环境或显示服务器扩展。Wayland 更碎片化,不同 compositor(如 GNOME、KDE、Sway)暴露方式不同。
常见路径:
- 读取环境变量:
QT_SCALE_FACTOR(Qt 应用)、GDK_SCALE(GTK 3.14+),但它们只反映 toolkit 自身缩放,不等于物理 DPI - 解析
xrandr --listmonitors输出 +xrandr --verbose中的mm尺寸,再结合分辨率算出近似 DPI(误差常达 ±10%) - 使用
libXft的XftDisplayInfo(需 Xft 初始化),但只对字体渲染有效,不反映全局 UI 缩放
结论:Linux 上无法像 Windows 那样拿到“系统级 UI 缩放比例”,只能按 toolkit 或桌面协议做适配,且必须接受多套逻辑共存。
macOS 用 [NSScreen backingScaleFactor]就行
macOS 的缩放由 Retina 屏幕和系统“缩放”设置共同决定,[NSScreen backingScaleFactor] 直接返回当前屏幕的缩放因子(如 2.0 表示 @2x,1.0 表示标准分辨率)。
注意点:
- 必须在主线程调用,且确保
NSScreen对象有效(例如[NSScreen mainScreen]或监听NSApplicationDidChangeMainScreenNotification) - 该值是浮点数,常见值为 1.0、2.0,部分外接显示器可能返回 1.25、1.5(取决于系统设置)
- 不要用
[NSScreen frame]和[NSScreen convertRectFromBacking:]手动推算,容易因坐标系混淆出错
跨平台应用里,macOS 这块最干净,但别忘了它和 Windows 的“DPI 数值”单位不同:macOS 返回缩放因子,Windows 返回 DPI 整数,换算时记得除以 96。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











