GetDpiForSystem是Windows 10 1703+/11获取系统默认DPI最直接方式,返回整数如120;Linux无统一API,需依X11/Wayland环境推算或查DBus;macOS用backingScaleFactor结合物理尺寸反推,推荐跨平台统一用缩放因子而非DPI数值。

Windows平台用GetDpiForSystem最直接
Windows 10 1703+ 和 Windows 11 原生支持每显示器 DPI 感知,GetDpiForSystem 是获取系统级默认 DPI 的最快方式,返回值是每英寸点数(如 96、120、144),无需缩放计算。
注意它不反映当前窗口的 DPI —— 如果你的程序未声明 DPI 感知(即没调用 SetProcessDpiAwareness 或没在 manifest 中配置),系统可能仍在模拟模式下运行,此时 GetDpiForSystem 返回的仍是 96,但界面已被拉伸。
- 必须链接
User32.lib - 需定义
WINVER≥ 0x0A00(即 Windows 10) - 若进程 DPI 感知等级为
PROCESS_SYSTEM_DPI_AWARE或更高,该函数才可靠;否则建议改用GetDpiForWindow配合有效窗口句柄
int dpi = GetDpiForSystem(); // 返回整数,如 120 float scale = dpi / 96.0f; // 得到缩放比例(1.25)
Linux上没有统一API,得看显示服务器和工具链
X11 下无法直接查“系统 DPI”,X server 本身不强制维护 DPI 设置;实际值通常来自 xrandr 输出的物理尺寸推算,或由桌面环境(GNOME/KDE)通过 D-Bus 暴露。
Wayland 更复杂:协议不传递 DPI,客户端只能靠环境变量(如 GDK_SCALE、QT_SCALE_FACTOR)或从 xdg-desktop-portal 查询,且这些只表示缩放因子,不是真实 DPI。
立即学习“C++免费学习笔记(深入)”;
- 用
xrandr --verbose查Monitor physical size和Current resolution,手动算:dpi = sqrt(w_px² + h_px²) / sqrt(w_mm² + h_mm²) * 25.4 - GNOME 用户可尝试 D-Bus 调用:
org.gnome.settings-daemon.plugins.xrandr的ScaleFactor属性(但非标准接口,版本间易变) - Qt 程序优先读
QScreen::logicalDotsPerInch(),它内部会尝试融合 X11/Wayland 信息,但底层仍可能 fallback 到硬编码 96
macOS用NSScreen的backingScaleFactor和物理尺寸组合
macOS 不暴露“DPI”概念,而是用 backingScaleFactor(通常是 2.0 表示 Retina)配合屏幕物理尺寸反推逻辑 DPI。系统认为“标准逻辑分辨率”对应 72 PPI,所以常用换算:logical_dpi = 72 * backingScaleFactor。
但要注意:macOS 的“逻辑坐标”与“点(point)”单位绑定,而点 ≠ 像素,backingScaleFactor 只说明像素密度倍率,并不等于 Windows 风格的 DPI 值(例如 144)。若你真需要匹配 Windows 行为(比如跨平台 UI 缩放),应统一用 backingScaleFactor 作为缩放系数,而非强行转成 DPI 数字。
- 必须在主线程调用
[NSScreen mainScreen],否则可能返回 nil -
physicalSize单位是毫米,需转为英寸(÷25.4)再参与计算 - 多屏场景下,每个
NSScreen *实例返回各自值,不能只查主屏
NSScreen *screen = [NSScreen mainScreen];
CGFloat scale = [screen backingScaleFactor]; // e.g., 2.0
CGSize sizeInMM = [screen physicalSize]; // e.g., {338.0, 211.0}
float dpi = 72.0 * scale / (sizeInMM.width / 25.4 / [screen frame].size.width);
跨平台封装时别硬套“DPI”术语,优先用缩放因子
真正影响渲染和布局的是缩放比例(scale factor),不是 DPI 数值本身。Windows 的 144 DPI、macOS 的 backingScaleFactor=2.0、Linux 的 GDK_SCALE=2,本质都是告诉 UI 框架“把 1 个逻辑单位画成 2 个物理像素”。强行统一成 “DPI” 反而引入转换误差和平台误解。
- Qt 提供
QGuiApplication::primaryScreen()->devicePixelRatio(),语义清晰且跨后端一致 - SDL2 用
SDL_GetDisplayDPI,但它在 Wayland/X11 上行为不同:X11 返回估算值,Wayland 可能返回 0 或 fallback 到 96 - 自己封装时,建议 API 返回
float get_ui_scale_factor(),内部按平台分支实现,对外不暴露 DPI 字样
最容易被忽略的一点:高 DPI 切换(比如拔掉外接屏)不会自动触发进程重绘,你得监听系统事件(如 Windows 的 WM_DPICHANGED、macOS 的 NSApplicationDidChangeScreenParametersNotification)并手动更新 UI 缩放逻辑。


















