最可靠方式是用getdpiforwindow通过窗口句柄获取实时dpi,缩放比=dpi/96;需声明permonitorv2清单并监听wm_dpichanged消息以响应动态缩放变化。

Windows平台下用GetDpiForWindow获取实时DPI缩放比例
在Windows 10(1703+)及Windows 11中,GetDpiForWindow是获取指定窗口当前DPI最直接、最可靠的方式。它返回的是每英寸点数(DPI),不是百分比;需除以96才能换算成缩放比例(如144 DPI → 144/96 = 1.5,即150%)。该函数支持Per-Monitor V2模式,能响应系统缩放变更(比如拖动窗口到不同DPI显示器时自动更新)。
关键前提:你的程序必须启用Per-Monitor DPI Awareness,并在manifest中声明或通过API设置:
- manifest中添加
<dpiaware>True/PM</dpiaware>或<dpiawareness>PerMonitorV2</dpiawareness> - 运行时调用
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)(推荐,更灵活) - 确保窗口已创建且句柄有效——不能在
WM_CREATE之前调用
为什么GetDpiForSystem不适合实时场景
GetDpiForSystem返回的是主显示器的系统级DPI,它不随窗口位置或动态缩放变化而更新。即使你把窗口拖到200%缩放的副屏上,该函数仍返回主屏的DPI(比如96),导致UI错位、字体模糊。
典型误用场景:
- 多显示器混合缩放(如主屏100%,副屏150%,再加一个200%的4K屏)
- 用户在设置中动态调整某显示器缩放比例后未重启应用
- 远程桌面或虚拟机中DPI策略与宿主不一致
此时GetDpiForSystem完全失效,必须绑定到具体窗口句柄。
跨Windows版本兼容性处理
Windows 8.1及更早系统不支持GetDpiForWindow,需降级 fallback。建议按以下顺序探测:
- 先尝试
GetDpiForWindow(Win10 1703+) - 失败则查
GetDpiForMonitor(需monitor.h,Win8.1+,但需手动获取monitor handle) - 再失败才用
GetDeviceCaps(hdc, LOGPIXELSX)(GDI方式,返回主屏DPI,仅作兜底)
注意:GetDpiForMonitor需要调用MonitorFromWindow + GetDpiForMonitor两步,且无法感知窗口跨屏瞬间的DPI切换——它只反映当前monitor的静态DPI,不如GetDpiForWindow精准。
监听DPI变更事件避免轮询
不要用定时器轮询DPI值。Windows提供WM_DPICHANGED消息,在窗口DPI实际变更时触发(例如拖入新显示器、系统设置调整后应用被通知)。处理要点:
- 在窗口过程(WndProc)中捕获
WM_DPICHANGED - 消息
wParam是新DPI值(如144),lParam低32位为新窗口矩形(需用ScaleWindowForDpiChange调整大小) - 收到后应重新计算字体大小、布局间距、图像缩放因子等,而非仅缓存DPI值
- 注意:该消息仅发给DPI-aware进程,非aware进程收不到
容易忽略的一点:WM_DPICHANGED不保证在窗口首次显示时触发——首次DPI需在WM_CREATE或WM_SHOWWINDOW后主动调用GetDpiForWindow获取。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











