You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

主显示器缩放超100%时GetMonitorInfo返回副屏尺寸错误导致无框WPF窗口最大化溢出

问题原因

这个问题是WPF默认的DPI感知机制和Windows的DPI虚拟化逻辑共同导致的:

  • 你的程序默认仅开启了系统级DPI感知,而非逐显示器级DPI感知。这种模式下Windows会统一以主显示器的缩放比例作为全局标准,所有外接显示器的坐标、尺寸都会经过主显示器缩放比例换算后再返回给程序接口。
  • 你主显示器缩放为250%,调用GetMonitorInfo获取副屏尺寸时,Windows返回的数值是按2.5倍放大后的逻辑尺寸,远大于副屏2560x1440的实际物理分辨率,因此最大化后窗口会超出显示范围。
  • 主显示器缩放设为100%时换算比例为1,返回的尺寸和实际尺寸一致,窗口就能正常显示。
修复方案

1. 开启逐显示器DPI感知

这是最根本的解决方法,在项目中添加应用程序清单(app.manifest),加入如下配置:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <!-- 其他已有配置 -->
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <!-- 兼容Win7及以上系统的逐显示器DPI感知 -->
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
      <!-- Win10 1703及以上系统推荐使用PerMonitorV2,支持动态DPI切换、弹出菜单缩放等特性 -->
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
    </windowsSettings>
  </application>
</assembly>

开启该配置后,GetMonitorInfo会直接返回当前所在显示器的真实物理坐标,无需额外换算即可正常最大化。

2. 手动做DPI换算(可选,兼容旧系统)

如果需要兼容不支持逐显示器DPI感知的旧系统,可以补充DPI换算逻辑,先获取当前显示器的缩放比例再对坐标做修正:
首先在FormMaximize类中添加DPI获取的API声明:

[DllImport("User32.dll")]
internal static extern uint GetDpiForMonitor(IntPtr hmonitor, int dpiType, out uint xDpi, out uint yDpi);
private const int MDT_EFFECTIVE_DPI = 0;

然后修改WmGetMinMaxInfo方法中的尺寸计算逻辑:

if (monitor != IntPtr.Zero)
{
    MonitorInfo monitorInfo = new MonitorInfo();
    GetMonitorInfo(monitor, monitorInfo);
    // 获取当前显示器的有效DPI计算缩放比例
    GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, out uint xDpi, out uint yDpi);
    double scaleFactor = xDpi / 96.0;
    
    Rectangle workArea = monitorInfo.WorkArea;
    Rectangle monitorArea = monitorInfo.MonitorArea;
    // 按比例修正坐标和尺寸
    minmaxInfo.MaxPosition.X = (int)Math.Round(Math.Abs(workArea.Left - monitorArea.Left) / scaleFactor);
    minmaxInfo.MaxPosition.Y = (int)Math.Round(Math.Abs(workArea.Top - monitorArea.Top) / scaleFactor);
    minmaxInfo.MaxSize.X = (int)Math.Round(Math.Abs(workArea.Right - workArea.Left) / scaleFactor);
    minmaxInfo.MaxSize.Y = (int)Math.Round(Math.Abs(workArea.Bottom - workArea.Top) / scaleFactor);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 01:15:01