GetDpiForWindow是Windows 10 1703+获取窗口实时DPI缩放因子最直接可靠的方式,返回逻辑/物理像素比(如144),需传入有效HWND、链接User32.lib并定义_WIN32_WINNT≥0x0A00。

Windows平台用GetDpiForWindow获取当前窗口实时DPI缩放因子
Windows 10 1703+ 支持 per-monitor DPI,GetDpiForWindow 是最直接、最可靠的运行时查询方式。它返回的是逻辑像素与物理像素的比率(即“DPI缩放因子”),不是原始DPI值(如96、120、144),而是整数倍数(如100% → 96,125% → 120,150% → 144,但函数返回的是 96、120、144 这类数值)。
关键点:
-
GetDpiForWindow必须传入一个有效的 HWND;传NULL或无效句柄会返回默认 DPI(通常是 96) - 该函数在多显示器场景下自动适配当前窗口所在屏幕的 DPI —— 不需要手动调用
MonitorFromWindow+GetDpiForMonitor - 返回值是每英寸点数(DPI),缩放比例 =
DPI / 96.0;例如返回144,则缩放比为1.5 - 必须链接
User32.lib,且项目需定义_WIN32_WINNT≥0x0A00(即 Windows 10)
// 示例:获取主窗口 DPI 缩放比 HWND hwnd = GetForegroundWindow(); // 或你自己的窗口句柄 UINT dpi = GetDpiForWindow(hwnd); // 如返回 144 double scale = static_cast<double>(dpi) / 96.0; // → 1.5
跨显示器时用GetDpiForMonitor配合MonitorFromWindow查指定屏参数
当需要知道某个显示器(而非当前窗口)的 DPI 配置,或想验证窗口是否已正确启用 Per-Monitor DPI 感知时,需组合使用 MonitorFromWindow 和 GetDpiForMonitor。
常见误区:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
GetDpiForMonitor的 monitor handle 不能硬编码或复用旧值 —— 显示器热插拔后 handle 失效,必须每次重新获取 - 第二个参数
MONITOR_DPI_TYPE推荐用MDT_EFFECTIVE_DPI(系统当前应用的缩放),避免用MDT_ANGULAR_DPI或MDT_RAW_DPI(后者含未校准的硬件原始值) - 即使程序声明了
PerMonitorV2感知,若窗口尚未完成 DPI 调整(比如刚移动到高DPI屏),GetDpiForWindow可能仍返回旧值;此时用GetDpiForMonitor更稳定
HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
UINT dpiX, dpiY;
if (SUCCEEDED(GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
double scale = dpiX / 96.0; // dpiX == dpiY for standard scaling
}
检查进程 DPI 感知模式:读取 manifest 或调用GetAwarenessFromDpiAwarenessContext
运行时 DPI 行为不仅取决于 API 调用,更取决于进程级 DPI 感知级别(Awareness)。错误的感知模式会导致 GetDpiForWindow 返回固定值(如始终 96),或触发系统位图拉伸。
判断依据:
- 用
GetDpiAwarenessContext获取当前线程上下文,再用GetAwarenessFromDpiAwarenessContext解析 —— 返回值如PROCESS_DPI_UNAWARE、PROCESS_PER_MONITOR_DPI_AWARE、PROCESS_PER_MONITOR_DPI_AWARE_V2 - 若返回
PROCESS_DPI_UNAWARE,说明 manifest 缺失或无效,或未调用SetThreadDpiAwarenessContext - 注意:
GetDpiAwarenessContext返回的是线程级上下文,主线程和 UI 线程必须显式设置(尤其在创建窗口前)
DPI_AWARENESS_CONTEXT ctx = GetThreadDpiAwarenessContext(); DPI_AWARENESS awareness = GetAwarenessFromDpiAwarenessContext(ctx); // awareness == DPI_AWARENESS_UNAWARE → 未启用 DPI 感知
Linux/macOS 上没有等价的“实时缩放因子”API,需降级处理
C++ 跨平台代码里,GetDpiForWindow 是 Windows 专属。Linux(X11/Wayland)和 macOS 没有统一、标准的运行时缩放因子查询接口:
- macOS:可用
NSScreen的backingScaleFactor,但仅适用于 Objective-C/Swift;C++ 需通过CFRunLoop或NSApp桥接,且无法在无 AppKit 环境下使用 - X11:部分 DE 提供
_NET_WM_ICON_SCALE或GTK_SCALE环境变量,但非强制标准,且不反映真实渲染缩放(如 HiDPI 屏幕可能设为 1 但实际用 2x 渲染) - Wayland:依赖
xdg-output协议,但需 Wayland client 库支持,且服务端未必实现;C++ 原生无裸 API - 结论:跨平台项目应把 Windows 作为唯一可信赖来源;Linux/macOS 建议 fallback 到硬编码缩放(如 1.0/2.0)或依赖 GUI 框架(Qt/SDL)封装层
真正容易被忽略的是:DPI 感知不是“查一次就完事”。窗口移动、显示器插拔、系统缩放设置变更都会触发 WM_DPICHANGED 消息 —— 必须响应此消息并重置字体、布局、图像尺寸,否则界面会错位或模糊。

















