Ошибка Screen.AllScreens и размещение WM_DISPLAYCHANGE в одном приложении WinForm - PullRequest
3 голосов
/ 26 октября 2011

Во-первых, извините за длинный пост.

Есть ли какие-либо предложения относительно того, как я могу ограничить область публикации сообщения WM_DISPLAYCHANGE?

Сценарий:

Screen.AllScreens возвращает массив координат и разрешений для всех мониторов, обнаруженных на клиенте.Если приложение запускается, когда рабочая станция заблокирована (во время перезапуска приложения на ночь), Screen.AllScreens возвращает только один элемент, детализирующий один экран с размерами всех нескольких мониторов, как один.

Впоследствии в этомВ сценарии, когда пользователь разблокирует рабочую станцию ​​и начинает использовать приложение, используемый элемент управления Infragistics (UltraWinDock) не позволяет перетаскивать плавающие окна за пределы основного экрана, поскольку свойство Screen.AllScreens не возвращает истинную конфигурацию монитора длясистема.Элемент управления Infragistics фактически смотрит на Screen.PrimaryScreen.Bounds, но свойство Screen.PrimaryScreen в свою очередь вызывает кэшированный массив Screen.AllScreens, который возвращает огромный первичный экран!

Когда приложение запускается нормально (с разблокированной рабочей станцией)), управление функционирует правильно.

Единственное средство, с помощью которого я вижу, что Screen.AllScreens сбрасывается и может быть обновлено, - это происходит через событие SystemEvents.DisplayChanging, после чего устанавливается внутреннее поле.к нулю.(Screen.AllScreens подключается к этому событию.) Screen.AllScreens будет заполнено при следующем вызове.

Из того, что я могу определить, событие SystemEvents.DisplayChanging может быть вызвано с помощью сообщения WM_DISPLAYCHANGE WMI.

Средство, с помощью которого я справился с обходным путем, - это вызов:

[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);

с параметром SM_CMONITORS , который представляет количество дисплеев в системе.По-видимому, это всегда возвращает фактическое количество присутствующих мониторов, независимо от того, заблокирована ли рабочая станция.

Затем я оцениваю, меньше ли длина массива Screen.AllScreens, чем результат GetSystemMetrics(SM_CMONITORS), и еслия подключаюсь к статическому событию SystemEvents.SessionSwitch и проверяю свойство SessionSwitchEventArgs.Reason на значение SessionUnlock.Когда рабочая станция разблокирована, это событие получено, и условие выполнено, поэтому я отправляю сообщение, используя метод P / Invoke

[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint wMsg, UIntPtr wParam, IntPtr lParam);

со следующими аргументами:

PostMessage(HWND_BROADCAST, WM_DISPLAYCHANGE,UIntPtr.Zero,IntPtr.Zero)

Это работает очень хорошо, и желаемый результат достигнут! Screen.AllScreens сбрасывается, и управление инфраструктурой работает правильно.

Мне кажется, что неясная ошибка с Screen.AllScreens не переоценивает себякогда приложение запускается на заблокированной рабочей станции, а затем разблокируется.

Редкая проблема, я подтверждаю, но, тем не менее, проблема.

Для сообщения WM_DISPLAYCHANGE , lParamи wParam описываются как:

wParam

  • Новая глубина изображения на дисплее, в битах на пиксель.

lParam

  • Слово младшего разряда определяет горизонтальное разрешение экрана.

  • Старшее слово определяет вертикальное разрешение экранаeen.

Я отправляю нулевые значения IntPtr.Zero для этих аргументов, поскольку я не знаю, каковы действительные значения на момент отправки сообщения.

Меня беспокоит то, что я передаю сообщение WM_DISPLAYCHANGE по всей системе с нулевыми аргументами и что могут быть запущенные процессы, которые используют WM_DISPLAYMESSAGE и используют аргументы.Я хотел бы надеяться, что при отправке нулевых аргументов любые потребители проигнорируют аргументы, но это очень опасное предположение.

Существует ли способ отправить или опубликовать сообщение только в приложении ввопрос и устранить риск влияния на другие процессы?

Я пробовал следующее безрезультатно:

PostMessage(IntPtr.Zero, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero)
PostMessage(this.Handle, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero)
PostThreadMessage(AppDomain.GetCurrentThreadId(), WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero)
SendMessage(this.Handle, WM_DISPLAYCHANGE, UIntPtr.Zero, IntPtr.Zero)

ПРИМЕЧАНИЯ:

  • У меня нет большого опыта работы с P / Invoke или WMI.
  • Целевой платформой является .Net 3.5.
  • Я пока не видел побочных эффектов от трансляции PostMessage метода WM_DISPLAYCHANGE.
  • Я уже скачал исходный код для Infragistics и точно определил, где проблема возникает в их коде,и подумал о перекомпиляции элемента управления, чтобы интегрировать исправление, но решил не делать этого.Я проинформировал Infragistics об этой проблеме, но не могу дождаться исправления и не рассматриваю ее как проблему Infragistics, поскольку она вызывает Screen.AllScreens.
  • Необходим перезапуск приложения в одночасье,и его нельзя изменить, чтобы дождаться, когда пользователь войдет в систему утром.
  • Я создал тестовое приложение, которое блокирует рабочую станцию ​​пользователя, перезапускает себя (приложение, а не рабочую станцию) и оценивает Screen.AllScreensсвойство, когда приложение заблокировано, а затем еще один моментальный снимок после отправки метода WM_DISPLAYCHANGE.Я хотел бы добавить скриншот, но я не могу, так как я являюсь новым пользователем StackOverflow !!!

Ответы [ 2 ]

0 голосов
/ 07 июля 2014

Возможно, поздно, чтобы дать ответ здесь, но другой вариант здесь будет просто P / Invoke тот же вызов, который использует AllScreens. Тогда вы получите фактическое значение вместо кэшированного значения, которое хранит AllScreens. Взгляните на: http://www.pinvoke.net/default.aspx/user32/EnumDisplayMonitors.html

Если вы находитесь в WPF, можете сделать что-то вроде того, что опубликовано здесь: http://social.msdn.microsoft.com/Forums/vstudio/en-US/41e0bf65-2e96-4d0f-98aa-2c0cf31aa493/wpf-application-controls-does-not-work-when-an-external-monitor-is-connected?forum=wpf

По сути, найдите SystemResourceNotifyWindow и отправьте ему сообщение WM_DISPLAYCHANGE.

0 голосов
/ 03 декабря 2011

Что мне просто (!) Нужно знать, как использовать P / Invoke для PostMessage для конкретного приложения

Вам нужно получить hwnd приложения, а не:

SendMessage(this.Handle...

Найдите класс окна приложения, используя spy ++, затем используйте FindWindow (), чтобы получить его hwnd.

Но, возможно, я кое-что здесь упускаю - вы, кажется, достаточно компетентны, чтобы понять это, так что, возможно, я неправильно понимаю, и что это за код, который вы имеете в своем перекомпилированном приложении Infragistics?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...