You need to enable JavaScript to run this app. C# 如何获取应用窗口当前所在显示器的精确DPI值
优惠活动
大模型
产品
解决方案
定价
更多

C# 如何获取应用窗口当前所在显示器的精确DPI值

窗口所在显示器DPI动态获取实现方案

前置配置调整

原有代码始终返回主显示器DPI的核心原因,绝大多数是应用的DPI感知模式不匹配,默认的系统感知模式(System Aware)下,系统会给应用返回启动时主显示器的固定DPI值,哪怕窗口拖动到其他屏幕也不会触发值更新。

  • 在项目的app.manifest配置中,将DPI感知模式修改为PerMonitorV2,避免系统强制锁定进程级固定DPI值,配置片段参考:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
  </windowsSettings>
</application>
  • 如果是WinForm项目,在应用入口的启动代码中,必须在实例化任何窗口之前调用PerMonitorV2开启方法:
// 必须放在Main方法最开头,早于Run()调用
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Application.EnableVisualStyles();
Application.Run(new MainForm());

核心DPI获取逻辑

不要依赖进程级的全局DPI变量,要以当前窗口的句柄为锚点,定位窗口所在的显示器,直接获取对应屏幕的实际DPI:

  • 引入两个必要的Win32 API方法:
// 根据窗口句柄获取窗口所在的显示器句柄
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
// 根据显示器句柄获取该屏的实际DPI值
[DllImport("Shcore.dll")]
static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out uint dpiX, out uint dpiY);
  • 封装可直接调用的获取方法,窗口跨屏时自动匹配对应屏的DPI:
const uint MONITOR_DEFAULTTONEAREST = 2; // 窗口跨屏时,取窗口中心最靠近的显示器
const int MDT_EFFECTIVE_DPI = 0; // 获取系统实际渲染用的有效DPI,和图像缩放需求完全匹配

public (float dpiX, float dpiY) GetCurrentWindowDpi(IntPtr windowHandle)
{
    // 定位当前窗口所在的显示器
    IntPtr currentMonitor = MonitorFromWindow(windowHandle, MONITOR_DEFAULTTONEAREST);
    // 获取对应显示器的DPI值
    GetDpiForMonitor(currentMonitor, MDT_EFFECTIVE_DPI, out uint dpiX, out uint dpiY);
    return (dpiX, dpiY);
}

动态更新触发

不要只在窗口初始化时获取一次DPI,要响应跨屏拖动时的DPI变更:

  • 重写主窗口的WndProc方法,监听系统主动推送的WM_DPICHANGED消息——当窗口从一个DPI的屏拖动到另一个屏时,系统会自动发送该消息,把新的DPI值直接带在消息参数中,不需要手动反复查询屏位置:
const int WM_DPICHANGED = 0x02E0;

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
    if (m.Msg == WM_DPICHANGED)
    {
        // 从消息参数中直接解析新的DPI值
        ushort newDpiX = (ushort)(m.WParam.ToInt64() & 0xFFFF);
        ushort newDpiY = (ushort)((m.WParam.ToInt64() >> 16) & 0xFFFF);
        // 触发图像渲染参数更新逻辑,把新的DPI值传给图像绘制模块
        UpdateImageRenderScale(newDpiX, newDpiY);
    }
}

常见踩坑提醒

  • 不要用主显示器的全局缩放值硬算副屏DPI,这种方法在Windows非整数缩放(如125%、175%)场景下会出现计算偏差,直接通过GetDpiForMonitor拿到的值是系统实际渲染用的真实值,无计算误差。
  • 不要在老版本.NET Framework中使用Control.DeviceDpi属性直接取值,这个属性在PerMonitorV2开启前会固定返回进程启动时的主屏DPI,不会随窗口跨屏更新。

内容的提问来源于stack exchange,提问作者Prithiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.08 08:15:04