#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?