我在我的程序中做了类似的事情:
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'。
我的显示器知道它没有连接(它已进入'节电'模式),控制面板知道它没有连接(它只显示一个显示器)。
那我错过了什么? 我如何确定只有一台显示器?
答案 0 :(得分:21)
我查看了源代码(请记住我们可以使用MS Symbol服务器执行此操作)。 AllScreens使用非托管API来获取第一个访问权限的屏幕,然后将结果存储在静态变量中供以后使用。
这样做的结果是,如果在程序运行时监视器的数量发生变化;然后Screen.AllScreens
将不会接受更改。
解决此问题的最简单方法可能是直接调用unmanaged API。
(或者你可能是邪恶的,并且在请求之前使用反射将静态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;
}
}
答案 1 :(得分: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
}
}
可能会有一些错别字,但这是基本的想法。祝你好运!
答案 2 :(得分: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