GetDpiForWindow 返回 0 表示窗口句柄无效或进程未声明 DPI 意识;需确保程序启动前通过 manifest(含 PerMonitorV2)或 SetProcessDpiAwarenessContext 显式启用高 DPI 感知,且 hwnd 必须为已创建的有效窗口句柄。

GetDpiForWindow 返回 0 怎么办
调用 GetDpiForWindow 却返回 0,基本等于没启用 DPI 意识。Windows 只对显式声明 DPI-aware 的进程才返回有效 DPI 值。.NET 5+ 默认是 unaware 模式,哪怕你写了 P/Invoke,系统也直接忽略请求。
必须在程序启动前完成声明,不是代码里调用一次就行。常见错误是只改了 app.manifest 但没在 csproj 里启用它:
- 确保
app.manifest文件存在,且内容包含<dpiAware>true/PM</dpiAware>或更推荐的<dpiAwareness>PerMonitorV2</dpiAwareness> - 在
.csproj中确认有<UseWPF>true</UseWPF>(WPF)或<UseWindowsForms>true</UseWindowsForms>(WinForms),否则 manifest 不生效 - 控制台程序需额外调用
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2),且必须在任何 UI 创建前执行
WinForms 下 GetDeviceCaps(HORZRES) 为什么不准
GetDeviceCaps 的 HORZRES 和 LOGPIXELSX 是 GDI 层面的逻辑分辨率,不反映当前窗口实际缩放比例。尤其多屏时,主屏缩放 125%,副屏 150%,同一个 GetDC 句柄可能返回主屏 DPI,完全不可靠。
正确做法是绑定到具体窗口句柄,并用 DPI-aware API:
- 优先用
GetDpiForWindow(hwnd)(Windows 10 1703+)——参数必须是已创建的窗口句柄,不能传 null 或控件 Handle 未初始化时的值 - 降级用
GetDpiForMonitor(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), ...),注意第二个参数必须是MDSIZING类型,否则返回主屏 DPI - WinForms 中可直接用
this.DeviceDpi(.NET 5+),但仅限当前窗体,且需确保窗体已显示(Load事件后才稳定)
WPF 的 VisualTreeHelper.GetDpi(this) 返回主屏 DPI
即使设置了 PerMonitorV2,VisualTreeHelper.GetDpi(this) 在跨屏拖动时仍可能卡在初始屏幕的 DPI,尤其当窗口尚未完成重绘或布局更新。
根本原因是 WPF 的 DPI 感知依赖于窗口消息循环中的 WM_DPICHANGED,而该消息只在窗口真正移动到新 DPI 区域并完成尺寸调整后才触发。常见误判点:
- 在
LocationChanged里立刻取 DPI —— 此时WM_DPICHANGED还没收到,值仍是旧的 - 没订阅
SourceInitialized+WM_DPICHANGED手动 Hook,导致首次跨屏无响应 - 使用
SystemParameters.PrimaryScreenWidth类静态属性 —— 它永远只读主屏,和当前窗口无关
稳妥方案:监听 WM_DPICHANGED,并在处理中调用 VisualTreeHelper.GetDpi(this),同时手动触发 InvalidateVisual() 强制重绘。
缩放后字体模糊、控件错位怎么快速定位
模糊本质是位图拉伸或文本未按 DPI 重绘;错位常因硬编码像素值(如 Width="200")没转为设备无关单位(DIP)。关键检查点:
- 所有
FontSize应基于DeviceDpi / 96.0动态缩放,别写死12—— WPF 中可用TextOptions.TextRenderingMode="ClearType"配合UseLayoutRounding="True" - WinForms 控件若用了
AutoScaleMode = Font,必须确保Font属性在InitializeComponent后被显式设置,否则缩放因子计算异常 - 自定义绘制(
OnPaint或DrawingContext)必须用RenderTransform或Graphics.ScaleTransform对齐 DPI,不能直接乘固定系数
最易被忽略的是资源加载路径:高 DPI 下用 Properties.Resources.xxx 加载的位图不会自动选 @2x,得手动判断 DPI 后拼接路径或改用矢量资源。


















