Screen.AllScreen未提供正确的监视器计数

问题描述 投票:15回答:3

我在我的程序中做了类似的事情:

Int32 currentMonitorCount = Screen.AllScreens.Length;

if  (currentMonitorCount < 2)
{
   //Put app in single screen mode.
}
else
{
   //Put app in dual screen mode.
}

非常重要我的应用程序可识别当前连接的监视器数量。

但是,在我多次插入/拔出显示器后,Screen.AllScreens.Length始终返回“2”。

我的显示器知道它没有连接(它已进入'节电'模式),控制面板知道它没有连接(它只显示一个显示器)。

那我错过了什么?我怎么知道只有一台显示器?

c# .net winforms windows-7 multiple-monitors
3个回答
21
投票

我看了一下源码(记住我们可以使用MS Symbol服务器来做到这一点)。 AllScreens使用非托管API在第一次访问时获取屏幕,然后将结果存储在静态变量中供以后使用。

这样做的结果是,如果在程序运行时监视器的数量发生变化;然后Screen.AllScreens不会接受改变。

解决这个问题的最简单方法可能是直接调用unmanaged API。 (或者你可能是邪恶的,并且在询问之前使用反射将静态screens字段设置为null。不要这样做)。

Edit:

如果您只需要知道计数,请在进入P / Invoke路线之前检查是否可以使用System.Windows.Forms.SystemInformation.MonitorCount(如评论中所示)。这直接调用GetSystemMetrics,它可能正确更新。

如果您发现需要使用P / Invoke进行此操作,这里有一个完整的示例演示了C#中非托管API的用法:

using System;
using System.Runtime.InteropServices;

class Program
{
    public static void Main()
    {
        int monCount = 0;
        Rect r = new Rect();
        MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) => ++monCount > 0;                                       
        if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0))
            Console.WriteLine("You have {0} monitors", monCount);
        else
            Console.WriteLine("An error occured while enumerating monitors");

    }
    [DllImport("user32")]
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData);

    private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData);

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

7
投票

在之前的driis回复的基础上,这是我处理它的方式。我应该注意以下代码存在于我的Program.cs文件中。

首先是外部资源和数据结构的链接:

    [DllImport("user32")]
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData);

    private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData);

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

现在创建一个包含监视器信息的简单对象:

public class MonitorInfo
{
    public bool IsPrimary = false;
    public Rectangle Bounds = new Rectangle();
}

还有一个容器来容纳这些对象:

    public static List<MonitorInfo> ActualScreens = new List<MonitorInfo>();

以及刷新容器的方法:

    public static void RefreshActualScreens()
    {
        ActualScreens.Clear();
        MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) =>
        {
            ActualScreens.Add(new MonitorInfo()
                {
                    Bounds = new Rectangle()
                    {
                        X = prect.left,
                        Y = prect.top,
                        Width = prect.right - prect.left,
                        Height = prect.bottom - prect.top,
                    },
                    IsPrimary = (prect.left == 0) && (prect.top == 0),
                });

            return true;
        };

        EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0);
    }

然后在表单上,​​如果我想检测到已添加或删除了显示...

    private const int WM_DISPLAYCHANGE = 0x007e;

    protected override void WndProc(ref Message message)
    {
        base.WndProc(ref message);

        if (message.Msg == WM_DISPLAYCHANGE)
        {
            Program.RefreshActualScreens();
            // do something really interesting here
        }
    }

可能是那里的一些错别字,但这是基本的想法。祝好运!


1
投票

我看过Screen类的代码(在here中)

参见第120行,Screen.AllScreens使用字段Screen.screens作为temp。在我的解决方案中,我使用反射api对Screen类进行一些更改!在调用Screen.AllScreen之前,我是干净的Screens.screens。

typeof(Screen).GetField("screens", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null);
// it is code for clean private field
© www.soinside.com 2019 - 2024. All rights reserved.