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

如何将DisplayMonitor映射到Forms.Screen并获取显示器工作区等信息?

解决DisplayMonitor与Forms.Screen的信息互补问题

问题1:能否将DisplayMonitor映射到Forms.Screen?

可以通过设备硬件标识符实现映射。虽然Forms.Screen.DeviceName仅返回\\.\DISPLAY1这类简化名称,但借助Win32 API可以获取每个屏幕对应的完整设备路径,与DisplayMonitor.DeviceId中的硬件标识片段匹配。

实现步骤与代码示例

先定义P/Invoke相关方法,用于获取屏幕的设备路径:

using System.Runtime.InteropServices;
using System.Text;

public static class ScreenHelper
{
    [DllImport("user32.dll")]
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);

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

    [DllImport("dxva2.dll", CharSet = CharSet.Unicode)]
    private static extern bool GetMonitorDevicePath(IntPtr hMonitor, StringBuilder pszDevicePath, ref int pcchDevicePath);

    private delegate bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);

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

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    public static string GetDevicePathForScreen(System.Windows.Forms.Screen screen)
    {
        var monitorInfo = new MONITORINFOEX();
        monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
        GetMonitorInfo(screen.GetHmonitor(), ref monitorInfo);
        
        var sb = new StringBuilder(1024);
        int length = sb.Capacity;
        GetMonitorDevicePath(screen.GetHmonitor(), sb, ref length);
        return sb.ToString();
    }
}

结合你的CollectMonitors方法实现映射:

private async Task MapMonitors()
{
    // 获取所有DisplayMonitor实例
    var displays = await DeviceInformation.FindAllAsync(DisplayMonitor.GetDeviceSelector());
    var displayMonitors = new List<DisplayMonitor>();
    foreach (var display in displays)
    {
        displayMonitors.Add(await DisplayMonitor.FromInterfaceIdAsync(display.Id));
    }

    // 获取所有Forms.Screen及其对应的设备路径
    var screens = System.Windows.Forms.Screen.AllScreens;
    var screenDevicePathMap = screens.ToDictionary(s => s, ScreenHelper.GetDevicePathForScreen);

    // 通过设备路径片段匹配两者
    var mappedPairs = new List<(DisplayMonitor DisplayMonitor, System.Windows.Forms.Screen Screen)>();
    foreach (var dm in displayMonitors)
    {
        var matchingScreen = screens.FirstOrDefault(s => dm.DeviceId.Contains(screenDevicePathMap[s]));
        if (matchingScreen != null)
        {
            mappedPairs.Add((dm, matchingScreen));
        }
    }

    // 使用组合信息:DisplayName + 工作区/主显示器状态
    foreach (var (dm, screen) in mappedPairs)
    {
        Console.WriteLine($"显示器名称: {dm.DisplayName}, 工作区: {screen.WorkingArea}, 是否主显示器: {screen.Primary}");
    }
}

问题2:无法映射时,如何从DisplayMonitor获取工作区?

若映射出现问题,可直接通过DisplayMonitor.DeviceId获取对应的Win32监视器句柄,再调用GetMonitorInfo获取工作区信息:

代码示例

public static System.Drawing.Rectangle GetWorkAreaFromDisplayMonitor(DisplayMonitor dm)
{
    IntPtr targetMonitorHandle = IntPtr.Zero;

    // 枚举所有监视器,匹配目标DisplayMonitor的设备路径
    ScreenHelper.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (hMon, hdcMon, ref ScreenHelper.RECT lprcMon, IntPtr dwData) =>
    {
        var sb = new StringBuilder(1024);
        int length = sb.Capacity;
        if (ScreenHelper.GetMonitorDevicePath(hMon, sb, ref length))
        {
            if (dm.DeviceId.Contains(sb.ToString()))
            {
                targetMonitorHandle = hMon;
                return false; // 找到后停止枚举
            }
        }
        return true;
    }, IntPtr.Zero);

    if (targetMonitorHandle == IntPtr.Zero)
        throw new InvalidOperationException("未找到对应监视器");

    // 获取包含工作区的监视器信息
    var monitorInfo = new ScreenHelper.MONITORINFOEX();
    monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
    ScreenHelper.GetMonitorInfo(targetMonitorHandle, ref monitorInfo);

    // 转换为Rectangle格式返回
    return new System.Drawing.Rectangle(
        monitorInfo.rcWork.Left,
        monitorInfo.rcWork.Top,
        monitorInfo.rcWork.Right - monitorInfo.rcWork.Left,
        monitorInfo.rcWork.Bottom - monitorInfo.rcWork.Top
    );
}

判断主显示器可通过GetMonitorInfo返回的dwFlags(MONITORINFOF_PRIMARY标记),或对比工作区与System.Windows.Forms.Screen.PrimaryScreen.WorkingArea是否一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 23:22:05