Экран.Ошибка AllScreens и публикация WM_DISPLAYCHANGE в одном приложении WinForm

#c# #wmi #pinvoke

#c# #wmi #pinvoke

Вопрос:

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

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

Сценарий:

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

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

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

Единственное средство, с помощью которого я вижу, что Screen.AllScreens это сбрасывается и может быть обновлено SystemEvents.DisplayChanging , — это вызываемое событие, после чего для внутреннего поля устанавливается значение null. ( 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 , из-за которой повторная оценка не выполняется при запуске приложения на заблокированной рабочей станции, а затем разблокирована.

A rare issue, I acknowldege, but an issue nonetheless.

For the WM_DISPLAYCHANGE message, lParam and wParam are described as:

wParam

  • The new image depth of the display, in bits per pixel.

lParam

  • The low-order word specifies the horizontal resolution of the screen.
  • Слово старшего порядка определяет вертикальное разрешение экрана.

Я отправляю нули 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 о проблеме, но не могу дождаться исправления и не рассматриваю это как проблему инфраструктуры, поскольку она Screen.AllScreens вызывает проблему.
  • Требуется перезапуск приложения в одночасье, и его нельзя изменить, чтобы дождаться входа пользователя в систему утром.
  • Я создал тестовое приложение, которое блокирует рабочую станцию пользователя, перезапускает себя (приложение, а не рабочую станцию) и оценивает Screen.AllScreens свойство, когда приложение заблокировано, а затем еще один снимок после отправки WM_DISPLAYCHANGE метода. Я хотел бы добавить скриншот, но мне не разрешено, поскольку я новый пользователь StackOverflow!!!

Комментарии:

1. Опять же, эта компания является крупной фабрикой ошибок. Решите свою реальную проблему, сбросьте их. Если вы не хотите, обратитесь за помощью к их каналам поддержки.

2. Да, я согласен, но, к сожалению, решение об использовании элементов управления не в моих руках, и каналы поддержки займут слишком много времени, хотя я зарегистрировал его с ними. Что мне просто (!) нужно знать, как использовать P / Invoke для отправки сообщений в конкретное приложение.

Ответ №1:

Вероятно, поздно давать ответ здесь, но другим вариантом здесь было бы просто вызвать / вызвать тот же вызов, который использует 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?форум = wpf

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

Ответ №2:

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

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

 SendMessage(this.Handle...
 

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

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