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

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

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’。

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

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

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

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

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

编辑:

如果您只需要知道计数,请检查在进入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; } } 

在之前的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 ActualScreens = new List(); 

以及刷新容器的方法:

  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 } } 

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

我看过Screen类的代码(在这里 )

参见第120行,Screen.AllScreens使用字段Screen.screens作为temp。 在我的解决方案中,我使用reflectionapi对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