windows平台下getdpiforwindow是获取窗口dpi最直接方式,返回整型dpi值(如144),需除以96得缩放因子(1.5);必须传入有效hwnd,且进程需启用permonitorv2感知并响应wm_dpichanged消息。

Windows平台下用GetDpiForWindow获取当前窗口DPI缩放因子
在Windows 10 1703+和Windows 11上,GetDpiForWindow是获取窗口实际DPI最直接的方式。它返回的是每英寸点数(DPI),不是百分比缩放值,需除以96才能得到缩放因子(例如144 DPI → 144/96 = 1.5)。
- 必须确保窗口已创建且句柄有效,传入
NULL或未初始化的HWND会返回0 - 该函数不触发DPI感知变更,仅读取当前状态;若窗口尚未完成DPI适配(如刚创建但未处理
WM_DPICHANGED),结果可能滞后 - 需要链接
User32.lib,并在编译时定义_WIN32_WINNT≥ 0x0A00(即Windows 10) - 示例:
int dpi = GetDpiForWindow(hwnd);<br>float scale = static_cast<float>(dpi) / 96.0f;</float>
如何判断进程是否启用Per-Monitor DPI Awareness
缩放因子是否随显示器变化,取决于进程的DPI感知级别。仅靠GetDpiForWindow无法得知当前行为是否“正确”,必须确认manifest或API设置是否匹配。
- 检查manifest中是否包含
<dpiaware>True/PM</dpiaware>,否则即使调用SetProcessDpiAwarenessContext也可能被忽略 - 运行时可用
GetProcessDpiAwarenessContext()获取当前上下文,返回DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2才支持V2级动态缩放 - 若返回
DPI_AWARENESS_CONTEXT_UNAWARE,所有GetDpiForWindow调用都返回主显示器DPI,与实际显示器无关 - 注意:
SetProcessDpiAwareness()在Windows 10 1607+已被弃用,应优先用SetProcessDpiAwarenessContext
监听DPI变化并响应WM_DPICHANGED消息
用户拖动窗口跨显示器、或系统调整缩放设置时,系统会发送WM_DPICHANGED。不处理它,UI就会模糊或错位。
- 消息参数:
wParam为新DPI值(如144),lParam低32位是推荐窗口大小(RECT*),需按此调整窗口尺寸 - 必须在消息处理中调用
SetWindowPos更新窗口大小,否则系统不会自动缩放客户区 - 不要在
WM_DPICHANGED里直接重绘——先调整布局、再触发InvalidateRect,否则可能触发多次无效绘制 - 如果使用Direct2D或DirectWrite,还需同步调用
ID2D1Factory::SetDpi更新渲染DPI
Linux/X11下通过_NET_WM_ICON_SCALE或scale-factor属性间接估算
Linux没有统一DPI API,Qt/GTK应用通常依赖环境变量或X11扩展,C++原生程序需组合多种信号判断。
- 检查
gdk_scale(GTK)或QT_SCALE_FACTOR环境变量,但它们只反映启动时值,不实时更新 - X11可监听
RANDR事件,读取XRandRGetScreenInfo中的mmWidth/mmHeight与width/height反推DPI,但需自行计算且不保证准确 - Wayland下更受限:只能从
xdg-output协议获取scale字段,且需客户端主动订阅输出事件 - 结论:Linux无可靠跨桌面环境的实时DPI查询接口,建议应用层缓存初始值 + 监听配置文件变更(如
~/.config/monitors.xml)作为fallback
WM_DPICHANGED消息处理逻辑,以及manifest中PerMonitorV2声明缺失导致GetDpiForWindow始终返回主屏DPI——这两个点一旦出错,高分屏上UI要么模糊要么错位,且很难复现到开发机上。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











