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

WPF切换主显示器后Screen类识别真实分辨率错误如何解决

问题原因

WinForms 内置的 Screen 类会对屏幕参数做内部缓存,系统显示设置(包括主显示器切换、分辨率、缩放比例变更)发生修改时,Screen 类不会主动刷新缓存,因此读取到的 Bounds 属性还是旧的数值,只有重启应用才会重新读取最新的显示参数。

解决方法

直接调用 Win32 API 绕过 Screen 类的缓存,读取系统当前生效的真实显示参数,无需重启应用。

完整代码实现

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public static class DisplayInfoHelper
{
    [DllImport("user32.dll", CharSet = CharSet.Ansi)]
    private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    private struct DEVMODE
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmDeviceName;
        public short dmSpecVersion;
        public short dmDriverVersion;
        public short dmSize;
        public short dmDriverExtra;
        public int dmFields;
        public int dmPositionX;
        public int dmPositionY;
        public int dmDisplayOrientation;
        public int dmDisplayFixedOutput;
        public short dmColor;
        public short dmDuplex;
        public short dmYResolution;
        public short dmTTOption;
        public short dmCollate;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmFormName;
        public short dmLogPixels;
        public int dmBitsPerPel;
        public int dmPelsWidth;
        public int dmPelsHeight;
        public int dmDisplayFlags;
        public int dmDisplayFrequency;
        public int dmICMMethod;
        public int dmICMIntent;
        public int dmMediaType;
        public int dmDitherType;
        public int dmReserved1;
        public int dmReserved2;
        public int dmPanningWidth;
        public int dmPanningHeight;
    }

    /// <summary>
    /// 获取指定显示器的真实物理分辨率(未经过缩放的原始分辨率)
    /// </summary>
    /// <param name="deviceName">显示器设备名,可从 Screen.DeviceName 属性获取</param>
    /// <returns>真实宽度、高度的元组</returns>
    public static (int PhysicalWidth, int PhysicalHeight) GetScreenRealResolution(string deviceName)
    {
        const int ENUM_CURRENT_SETTINGS = -1;
        DEVMODE devMode = new DEVMODE();
        devMode.dmSize = (short)Marshal.SizeOf(devMode);

        if (EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, ref devMode))
        {
            return (devMode.dmPelsWidth, devMode.dmPelsHeight);
        }

        return (0, 0);
    }
}

调用示例

你已经可以通过 Screen.PrimaryScreen.DeviceName 正确拿到当前主显示器的设备名,直接传入方法即可:

// 切换主显示器后直接调用即可拿到正确的真实分辨率
var primaryDeviceName = Screen.PrimaryScreen.DeviceName;
var (realWidth, realHeight) = DisplayInfoHelper.GetScreenRealResolution(primaryDeviceName);

额外优化

可以订阅系统显示设置变更事件,在主显示器切换、分辨率修改时自动更新分辨率数值,无需主动轮询:

Microsoft.Win32.SystemEvents.DisplaySettingsChanged += (sender, args) =>
{
    // 显示设置变更后重新读取主显示器分辨率即可
    var primaryDeviceName = Screen.PrimaryScreen.DeviceName;
    var (realWidth, realHeight) = DisplayInfoHelper.GetScreenRealResolution(primaryDeviceName);
    // 这里处理后续的业务逻辑
};

注意事项

请确保项目已开启 DPI 感知,可在项目的 app.manifest 中取消以下配置的注释:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
  </windowsSettings>
</application>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 12:36:04