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

C#如何获取与Windows显示设置一致的屏幕排列及主副屏信息

你遇到的System.Windows.Forms.Screen类返回结果和系统显示设置不一致的问题,是该类的已知缺陷:它基于老旧的GDI接口实现,内部会静态缓存显示设备信息,仅在进程启动时读取一次,后续修改显示配置(切换主屏、调整排列、插拔显示器)后不会主动刷新,同时高DPI场景下也可能返回缩放后的错误坐标。

以下是完全和Windows显示设置面板结果一致的可靠解决方案:

方案1:调用Win32原生API(兼容性最好,支持Win7及以上所有系统)

直接使用Windows显示子系统原生API,和系统设置面板使用完全相同的数据源,结果100%一致。

第一步:定义P/Invoke声明和结构体

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public static class DisplayApi
{
    [DllImport("user32.dll")]
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
    private delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct MONITORINFOEX
    {
        public uint cbSize;
        public RECT rcMonitor;
        public RECT rcWork;
        public uint dwFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szDevice;
    }

    private const uint MONITORINFOF_PRIMARY = 0x00000001;

    // 对外暴露的显示器信息结构
    public class DisplayInfo
    {
        public string DeviceName { get; set; }
        public int BoundsLeft { get; set; }
        public int BoundsTop { get; set; }
        public int BoundsWidth { get; set; }
        public int BoundsHeight { get; set; }
        public int WorkAreaLeft { get; set; }
        public int WorkAreaTop { get; set; }
        public int WorkAreaWidth { get; set; }
        public int WorkAreaHeight { get; set; }
        public bool IsPrimary { get; set; }
    }

    // 获取所有显示器最新信息的方法
    public static List<DisplayInfo> GetAllDisplays()
    {
        List<DisplayInfo> displays = new List<DisplayInfo>();
        EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (IntPtr hMonitor, IntPtr hdc, ref RECT rc, IntPtr data) =>
        {
            MONITORINFOEX mi = new MONITORINFOEX();
            mi.cbSize = (uint)Marshal.SizeOf(mi);
            if (GetMonitorInfo(hMonitor, ref mi))
            {
                displays.Add(new DisplayInfo
                {
                    DeviceName = mi.szDevice,
                    BoundsLeft = mi.rcMonitor.left,
                    BoundsTop = mi.rcMonitor.top,
                    BoundsWidth = mi.rcMonitor.right - mi.rcMonitor.left,
                    BoundsHeight = mi.rcMonitor.bottom - mi.rcMonitor.top,
                    WorkAreaLeft = mi.rcWork.left,
                    WorkAreaTop = mi.rcWork.top,
                    WorkAreaWidth = mi.rcWork.right - mi.rcWork.left,
                    WorkAreaHeight = mi.rcWork.bottom - mi.rcWork.top,
                    IsPrimary = (mi.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY
                });
            }
            return true;
        }, IntPtr.Zero);
        return displays;
    }
}

第二步:调用示例

foreach (var display in DisplayApi.GetAllDisplays())
{
    Console.WriteLine($"Device Name: {display.DeviceName}");
    Console.WriteLine($"Bounds: X={display.BoundsLeft}, Y={display.BoundsTop}, Width={display.BoundsWidth}, Height={display.BoundsHeight}");
    Console.WriteLine($"Working Area: X={display.WorkAreaLeft}, Y={display.WorkAreaTop}, Width={display.WorkAreaWidth}, Height={display.WorkAreaHeight}");
    Console.WriteLine($"Primary Screen: {display.IsPrimary}");
    Console.WriteLine("=====================================================");
}

必须注意的配置项

必须为程序开启Per-Monitor DPI感知,否则获取到的坐标会被系统DPI缩放,导致和设置面板显示的数值不一致,可在项目的app.manifest中添加如下配置:

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

该方案每次调用都会主动读取当前最新的显示配置,不存在缓存问题,修改显示设置后直接重新调用即可拿到最新结果。

备选方案(Win10 1809及以上版本可用)

可以使用Windows Runtime提供的Windows.Devices.Display.Core.DisplayManager类,无需P/Invoke,直接调用即可获取最新的显示配置,结果同样和系统设置完全一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 07:45:00