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".
Мой монитор знает, что он не подключен (он включил режим энергосбережения), и панель управления знает, что она не подключена (отображается только один монитор).
Так что мне не хватает? Как я могу понять, что есть только один монитор?
Ответы
Ответ 1
Я посмотрел на источник (помните, что мы можем это сделать с помощью серверов Symbol MS). AllScreens использует неуправляемый API для получения экранов при первом доступе, а затем сохраняет результат в статической переменной для последующего использования.
Следствием этого является то, что если количество мониторов изменяется во время работы вашей программы; то Screen.AllScreens
не получит изменения.
Самый простой способ обойти это, вероятно, можно было бы напрямую вызвать неуправляемый API.
(Или вы можете быть злым и использовать отражение, чтобы статическое поле screens
было нулевым, прежде чем спрашивать. Не делайте этого).
Изменить:
Если вам просто нужно знать количество, проверьте, можете ли вы использовать System.Windows.Forms.SystemInformation.MonitorCount
(как это предложено в комментариях) перед тем, как перейти к маршруту P/Invoke. Это вызывает GetSystemMetrics напрямую и, вероятно, правильно обновляется.
Если вы обнаружите, что вам нужно сделать это с помощью P/Invoke, вот полный пример, демонстрирующий использование неуправляемого 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;
}
}
Ответ 2
Основываясь на предыдущем ответе на 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
}
}
Возможно, там будет несколько опечаток, но это основная идея. Удачи!
Ответ 3
Я посмотрел код класса Screen (в здесь)
См. строку 120, Screen.AllScreens. Используйте поле Screen.screens для temp.
И в моем решении, я использую отражение api, чтобы внести некоторые изменения в класс Screen!!
Я чистый Screen.screens перед отзывом Screen.AllScreen.
typeof(Screen).GetField("screens", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null);
// it is code for clean private field