Blog. Just Blog

Программное выключение нескольких мониторов

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Программное выключение нескольких мониторов
Программное выключение нескольких мониторов

На технических форумах в этих ваших интернетах периодически поднимается тема о программном выключении мониторов. Предлагаются разные решения, чаще всего просто бездумно скопированные с других сайтов или более-менее адаптированные под требуемый язык программирования. В свое время я тоже делал что-то подобное. Сегодня хотелось бы провести глобальную работу над ошибками и выдать "на гора" действительно правильное и рабочее решение этой задачи. Заодно я постараюсь подробно рассказать, что не так с другими предлагаемыми решениями.

Начнем с теории. В Windows Vista появились целая группа функций, позволяющих управлять основными настройками монитора на высоком уровне, а также несколько низкоуровневых функций. Вот они-то нам и нужны. С помощью функции GetVCPFeatureAndVCPFeatureReply мы получаем значения нужных нам параметров Virtual Control Panel (VCP), а при помощи функции SetVCPFeature меняем эти значения. Таким образом мы можем, например, определить производителя монитора, узнать текущее состояние электропитания физического монитора, и выполнить то, ради чего эта статья и затевалась, а именно выключить монитор. Наиболее полный и актуальный список всех кодов VCP можно найти в мануале VESA Monitor Control Command Set (MCCS). Этот документ предназначен для использования только внутри компании VESA, но мир не без добрых людей, подтянули, поделились.

VESA Monitor Control Command Set (ENG)VESA Monitor Control Command Set (ENG)

VESA.Monitor.Control.Command.Set.zip (2,037,600 bytes)

Важное замечание. Управление при помощи кодов VCP возможно только для мониторов, которые поддерживают стандарт VESA DDC/CI. Это относится практически ко всем современным мониторам, даже мой безымянный китайский монитор соблюдает этот стандарт. Вместе с тем, некоторые производители поддерживают свой набор кодов. Мониторы Samsung, например, можно перевести в спящий режим стандартными кодами, а вот выключить питание только своими собственными.

FASM из коробки не знает многие константы и структуры, это касается и нашего случая. Не проблема, определим их самостоятельно.
  1. ; Стандартные значения параметра Power Mode
  2. POWER_ON        = 0x01
  3. POWER_STANDBY   = 0x02
  4. POWER_SUSPEND   = 0x03
  5. POWER_OFF       = 0x04
  6. ; Нестандартное значение параметра Power Mode
  7. FORCE_POWER_OFF = 0x05
  8.  
  9. ; Структура для получения физического монитора
  10. struct PHYSICAL_MONITOR
  11.         hPhysicalMonitor dd ?
  12.         szPhysicalMonitorDescription rw 128
  13. ends
Разбор ошибок начнем с того, как правильно определить все мониторы, которые затем надо выключить. Недостаточно просто получить хендл рабочего стола и определить монитор, который ему соответствует. Дело в том, что в системе может быть несколько рабочих столов, в зависимости от настроек каждому столу может соответствовать свой логический монитор, а ему, в свою очередь, несколько физических мониторов или ничего, если логический монитор виртуальный. Поэтому, чтобы получить список всех логических мониторов, установленных в системе, воспользуемся функцией EnumDisplayMonitors. В указанную в параметрах callback-функцию она поочередно передает хэндлы всех имеющихся логических мониторов.
  1.         ; Перебрать все мониторы, установленные в системе
  2.         invoke  EnumDisplayMonitors,NULL,NULL,MonitorEnumProc,NULL
Callback-функцию я приведу целиком, а затем прокомментирую, что и как там работает. Функция самодостаточная, не требует никаких дополнительных данных.
  1. ;--------------------------------------------------------
  2. ; Callback-функция перебора мониторов
  3. ;--------------------------------------------------------
  4. proc MonitorEnumProc hMonitor:DWORD,hdc:DWORD,lpRect:DWORD,lParam:DWORD
  5.         locals
  6.             ; Количество мониторов
  7.             num dd ?
  8.  
  9.             ; Массив PHYSICAL_MONITOR
  10.             hMonArray dd ?
  11.             MonArray  dd ?
  12.  
  13.             CurrentValue dd ?
  14.             MaximumValue dd ?
  15.             CurrentS dd ?
  16.             MaximumS dd ?
  17.         endl
  18.  
  19.         ; Получить количество физических мониторов
  20.         lea     eax,[num]
  21.         invoke  GetNumberOfPhysicalMonitorsFromHMONITOR,[hMonitor],eax
  22.  
  23.         ; Физических мониторов нет
  24.         cmp     [num],0
  25.         je      .loc_ret
  26.  
  27.         ; Зарезервировать память под массив PHYSICAL_MONITOR
  28.         mov     eax,[num]
  29.         imul    eax,sizeof.PHYSICAL_MONITOR
  30.  
  31.         invoke  GlobalAlloc,GMEM_MOVEABLE+GMEM_DDESHARE,eax
  32.         mov     [hMonArray],eax
  33.         invoke  GlobalLock,[hMonArray]
  34.         mov     [MonArray],eax
  35.  
  36.         ; Получить информацию о всех физических мониторах
  37.         invoke  GetPhysicalMonitorsFromHMONITOR,[hMonitor],[num],[MonArray]
  38.  
  39.         ; Выключить все мониторы поочередно
  40.         mov     ecx,[num]
  41.         mov     esi,[MonArray]
  42.  
  43. .loc_monitors_loop:
  44.         push    ecx
  45.  
  46.         ; Получить текущее состояние монитора
  47.         lea     eax,[MaximumValue]
  48.         push    eax
  49.         lea     eax,[CurrentValue]
  50.         push    eax
  51.         invoke  GetVCPFeatureAndVCPFeatureReply,\
  52.                 [esi+PHYSICAL_MONITOR.hPhysicalMonitor],0D6h,NULL
  53.  
  54.         ; Монитор вообще поддерживает код 0D6h?
  55.         cmp     eax,1
  56.         jne     .loc_next_monitor
  57.  
  58.         ; Монитор включен?
  59.         cmp     [CurrentValue],POWER_ON
  60.         jne     .loc_next_monitor
  61.  
  62.         ; Значения выходят за допустимые границы
  63.         cmp     [MaximumValue],5
  64.         ja      .loc_next_monitor
  65.         cmp     [MaximumValue],0
  66.         je      .loc_next_monitor
  67.  
  68.         ; Это монитор Samsung?
  69.         lea     eax,[MaximumS]
  70.         push    eax
  71.         lea     eax,[CurrentS]
  72.         push    eax
  73.         invoke  GetVCPFeatureAndVCPFeatureReply,\
  74.                 [esi+PHYSICAL_MONITOR.hPhysicalMonitor],0E1h,NULL
  75.         ; Код 0E1h не поддерживается, это точно не Samsung
  76.         cmp     eax,1
  77.         jne     @f
  78.  
  79.         ; Максимальное значение не 1, это точно не Samsung
  80.         cmp     [MaximumS],1
  81.         jne     @f
  82.  
  83.         ; Максимальное значение не 4, это точно не Samsung
  84.         cmp     [MaximumValue],4
  85.         jne     @f
  86.  
  87.         ; Передать команду выключения монитора Samsung
  88.         invoke  SetVCPFeature,[esi+PHYSICAL_MONITOR.hPhysicalMonitor],0E1h,0
  89.         jmp     .loc_next_monitor
  90. @@:
  91.         ; Передать общую команду выключения монитора
  92.         invoke  SetVCPFeature,[esi+PHYSICAL_MONITOR.hPhysicalMonitor],\
  93.                 0D6h,[MaximumValue]
  94.  
  95. .loc_next_monitor:
  96.         ; Следующая запись из массива PHYSICAL_MONITOR
  97.         add     esi,sizeof.PHYSICAL_MONITOR
  98.         pop     ecx
  99.         sub     ecx,1
  100.         jnz     .loc_monitors_loop
  101.  
  102.         ; Прибраться за собой
  103.         invoke  DestroyPhysicalMonitors,[num],[MonArray]
  104.  
  105.         ; Освободить память
  106.         invoke  GlobalUnlock,[hMonArray]
  107.         invoke  GlobalFree,[hMonArray]
  108.  
  109. .loc_ret:
  110.         ; Продолжаем обработку
  111.         mov     eax,TRUE
  112.         ret
  113. endp
Для каждого хэндла логического монитора получаем соответствующее ему количество физических мониторов при помощи функции GetNumberOfPhysicalMonitorsFromHMONITOR. Затем выделяем память под массив структур PHYSICAL_MONITOR и заполняем ее при помощи функции GetPhysicalMonitorsFromHMONITOR. После этого поочередно проверяем состояние каждого найденного физического монитора и при необходимости выключаем его, меняя значение параметра электропитания функцией SetVCPFeature.

Вернемся к работе над ошибками. Везде, в каждом виденном мной примере для выключения монитора используется VCP-код D6h (Power Mode) со значением 4. Но если открыть мануал на 70-й странице, то там обнаружится интересное дополнение.


05h - Power off the display - functionally equivalent to turning off power using the "power button"


То есть значение 5 выключает монитор аналогично нажатию физической кнопки питания. Если монитор поддерживает такое "холодное" выключение, то надо использовать именно его. При попытке применить на таких мониторах значение 4 кода Power Mode, монитор фактически не выключится, а перейдет в режим какого-то глубокого коматоза. Вывести монитор из этого состояния можно только окончательно выключив его кнопкой питания и включив заново. Поскольку значение 5 обозначено как нестандартное, перед его использованием требуется проверить, поддерживает ли монитор такое "холодное" выключение или нет. Делается это при помощи функции GetVCPFeatureAndVCPFeatureReply с кодом D6h. Функция вернет текущее состояние питания монитора и максимальное значение параметра Power Mode, поддерживаемое монитором. Если максимальное значение получилось 5, то можно смело выключать монитор "холодным" способом, иначе придется довольствоваться четвертым режимом. Дополнительно надо проверять текущее состояние питания, если оно не равно POWER_ON, то монитор уже находится не во включенном состоянии, а значит ничего с ним делать не надо. Как показали эксперименты, обязательно надо проверять, успешно или нет отработала функция GetVCPFeatureAndVCPFeatureReply. Например, на ноутбуках код D6h вообще не поддерживается, как и на мониторах старых моделей. В таких случаях найденный физический монитор надо пропускать.

Теперь что касается мониторов Samsung. Как показали мои эксперименты, эти мониторы поддерживают максимальное значение параметра Power Mode равное 4, а для "холодного" выключения используют собственный VCP-код E1h со значением параметра 0. Использование кода Power Mode с параметром 4 могут отправить некоторые модели мониторов в описанное выше коматозное состояние. Каким образом определить, что это монитор Samsung? Для этого надо запросить результат функции GetVCPFeatureAndVCPFeatureReply по VCP-коду E1h. Если код не поддерживается, значит монитор точно не Samsung. Максимальное значение при этом должно быть не больше 1. Но одной этой проверки мало, к примеру, мой рабочий монитор Dell тоже технически поддерживает этот код, но никак на него не реагирует. Поэтому, чтобы с большой уверенностью считать, что перед нами монитор Samsung, должны быть выполнены все три условия, а именно: максимальное значение Power Mode равно 4, результат запроса функции по коду E1h вернул TRUE и максимальное значение по этому коду равно 1. К сожалению, у меня не так много подопытных, поэтому я не могу с гарантией утверждать, что это справедливо для всех мониторов Samsung. В связи с этим у меня огромная просьба к обладателям мониторов Samsung. В приложении к статье есть программа info.exe, которая сохраняет в файл названия мониторов и их параметры, нужные мне. Напишите, пожалуйста, в комментариях свои результаты из файла, я буду вам очень признателен.

В приложении пример программы с исходным текстом, которая пытается выключить все найденные физические мониторы. Если что-то будет работать не так, то там же в архиве есть программа info.exe, которая записывает в файл диагностическую информацию о мониторах. Присылайте мне ее результаты, буду разбираться.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Advanced.Monitor.OFF.Demo.zip (3,476 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 586 | Комментариев: 39

Комментарии

Отзывы посетителей сайта о статье
ManHunter (15.11.2019 в 22:41):
Ну вот, VESA Commander в первом приближении готов, теперь можно творить со своими мониторами любые непотребства. До официального релиза выкладывать не буду, кому реально надо - пишите на обратную связь, пришлю файл поиграться и инструкции чо как.

Rustamer, Павел, жду первых бета-тестеров
ManHunter (14.11.2019 в 22:21):
Мне проще написать какой-нибудь "VESA Commander", чтобы вы передавали ему на каком мониторе, какие коды и в какое значение ставить, сами проводили эксперименты, вешали запуск на хоткеи и т.п. Один только код 0C0h чего стоит :)) А то выполнить все пожелания просто нереально.
Павел (14.11.2019 в 20:50):
Интересная тема, вот что думаете по такой штуке - подключено к компу скажем так два монитора (один из них телек по hdmi в моем случае, но наверно это не очень принципиально) Можно ли выключить или даже лучше отправить все же в спячку один из них, по хоткею? ну и потом обратно. а другой пусть работает. Это как идея для будущего программного релиза - такая штука будет востребована точно
p.s. говорю как юзер Ваших полезных программулин :) teamview tamper у меня на всех компах в автозагрузке:)
Rustamer (14.11.2019 в 19:41):
ManHunter, про сову и глобус не соглашусь. Какой смысл в покупать переключатель, да еще и с DisplayPort-ом, когда можно все щелчком мыши переключить или по хоткею? Не так уж давно открыл для себя программные свитчи (Input Director и иже с ними) - они работают по сети прозрачно. В результате дома можно обходиться одной мышью и клавиатурой, а к ноутбуку ничего не подключать (кроме HDMI и зарядки). Точно также и на работе применяю с ноутбуком и десктопом. Раздражает только необходимость переключения монитора дома. Хотел накатать утиль для этого.
Но кажется, что меня кое-кто опередил :) Спасибо!
Работает хорошо, но только в одну сторону - с HDMI на DisplayPort (==запускаю на ноуте). А вот напрямую не желает - никакой реакции.
SE370_S27E370D (DisplayPort) - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 1 - MaxS: 1
ManHunter (14.11.2019 в 10:53):
Rustamer, зачем натягивать сову на глобус? КВМ-свичи на том же алиэкспрессе от 500 рублей. https://www.aliexpress.com/ite...6027418.html
А по сути вопроса VCP-код 60h - Input Select. Страница 81 в мурзилке. Проверил, на моем мониторе Dell программное переключение работает.

Proof of Concept. Переключает монитор с HDMI1 на DP1 и обратно:
https://www.upload.ee/files/10...dp1.zip.html
Rustamer (14.11.2019 в 10:22):
Не по теме выключения, но тоже в контексте  DDC/CI ищу возможность переключать текущий вход через DDC/CI из консоли/кода. Нередко приходится переключать монитор с десктопа на ноутбук и наоборот. К первому монитор подключен через DisplayPort, а ко второму через HDMI. Или такое переключение нереально через DDC/CI?
ManHunter (13.11.2019 в 21:41):
monitoroff.exe не выключает, а переводит в спящий режим. Монитор просыпается от движения мышки или от кнопки клавиатуры. Но это единственное решение для старых мониторов.
coldun (13.11.2019 в 02:03):
Hisense LEDN24K15P - монитор никак не реагирует, info.exe- создаётся ПУСТОЙ info.txt.
В свое время я тоже делал что-то подобное - по этой ссылке в начале статьи программка monitoroff.exe- монитор отключает.
TestLog (11.11.2019 в 08:28):
ViewSonic VA2213

Универсальный монитор PnP - ID: 0012h - Cur: 1 - Max: 4 - ResS: 0 - CurS: 0 - MaxS: 0
выключился, кнопка погасла, включился с кнопки

Попозже музейный relisys проверю, пока времени нет.
Евгений (11.11.2019 в 04:43):
Монитор Acer
Generic PnP Monitor - ID: 0009h - Cur: 1 - Max: 4 - ResS: 0 - CurS: 0 - MaxS: 0

Выключается, светодиод горит, включается кнопкой
TestLog (09.11.2019 в 13:06):
LG Flatron W2343S

Универсальный монитор PnP - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 0 - MaxS: 0
выключился, кнопка погасла, включился с кнопки
ManHunter (09.11.2019 в 00:14):
Ну вот, теоретические выкладки из статьи подтверждаются с каждым новым тестом. ОГРОМНОЕ СПАСИБО всем! Но если не сложно, то с радостью приму любые другие результаты тестов, т.к. мой служебный зоопарк техники довольно ограничен в плане уникальности моделей, закупали сразу много и одинаковых.
Владимир_5HDD (09.11.2019 в 00:08):
Samsung SyncMaster 971p
Универсальный монитор PnP - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 1 - MaxS: 1
Выключился полностью - светодиод не мигал, включил по кнопке.
ManHunter (09.11.2019 в 00:07):
Такая же хрень - старый монитор. Не поддерживает стандарт DDC/CI. Протестировал еще на своем старом ViewSonic, программное выключение не поддерживает. Максимум, что можно из него выжать - это спячка через отправку SC_MONITORPOWER.
wet (08.11.2019 в 23:04):
С монитором Acer программа не работает.
Создается файл info.txt размером в 0 байт.
Николай (08.11.2019 в 15:23):
Монитор BENQ, 19", 5:4
Generic PnP Monitor - ID: 0000h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 100 - MaxS: 100
Выключается, светодиод не горит. Включается кнопкой на мониторе.
ManHunter (07.11.2019 в 15:49):
Exit, специально для аськи и локалки: https://www.upload.ee/files/10...off.zip.html
Exit (07.11.2019 в 15:46):
ManHunter,
Просто вспомнилось, что по локалке и в аське бродила "шутка" - прикол.vbs
Запустив этот "прикол", начинал выезжать и заезжать лоток CD Rom"а
с выводом MsgBox"а - "ХА-ХА-ХА  на ближайшие 10  мин. ты будешь злится на меня-100% зацени сейчас свой дисковод"
Ну или комп в ребут пускали.
P.S. пунктуация и стилистика афтара, сохранена ))
Василий (07.11.2019 в 15:41):
LG 24MK430H
Универсальный монитор PnP - ID: 0012h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 0 - MaxS: 65535
Выключается, лампочка не мигает, обратно включается с кнопки)))
ManHunter (07.11.2019 в 14:38):
Переименовывать не надо, у меня параллельно делается нормальный релиз программного включения-выключения мониторов, с иконками, манифестами, все дела. Но сперва надо тут довести все до ума.
Exit (07.11.2019 в 14:32):
Самсунг S24B300BL
Generic PnP Monitor - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 1 - MaxS: 1
Выключается, лампочка не мигает, обратно включается с кнопки.

P.S. ну все, осталось переименовать monitoroffex.exe в "котики.jpg.exe" и пустить в массы )))))))
ManHunter (07.11.2019 в 13:53):
ч.т.д.
Never (07.11.2019 в 13:53):
Да, он древний, еще 4:3.
ManHunter (07.11.2019 в 13:52):
Значит монитор не поддерживает стандарт DDC/CI и программно его не выключить. У меня на старом BenQ такая же фигня, все по нулям.
Never (07.11.2019 в 13:50):
Монитор, ты же предупредил, что на ноутах не работает
Владимир (07.11.2019 в 13:41):
Samsung SyncMaster 943n
Generic PnP Monitor - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 1 - MaxS: 1
Выключается, лампочка не мигает, обратно включается с кнопки.
Андрей (07.11.2019 в 13:34):
SE360_S24E391HL (Analog) - ID: 0005h - Cur: 1 - Max: 4 - ResS: 1 - CurS: 1 - MaxS: 1
Теперь отключается полностью (лампочка не мигает)
ManHunter (07.11.2019 в 13:20):
Это монитор или ноут? Если ноут, то для них тут ничего не светит.
Never (07.11.2019 в 13:15):
Sony не пошло
Универсальный монитор PnP - ID: 0000h - Cur: 0 - Max: 0 - ResS: 1 - CurS: 0 - MaxS: 0
ManHunter (07.11.2019 в 11:17):
Убрал определение самсунгов по ID производителя, добавил проверку по коду E1h. Статью и примеры обновил.

Рабочий монитор:
Dell U2415(HDMI1) - ID: 1109h - Cur: 1 - Max: 5 - ResS: 1 - CurS: 0 - MaxS: 1
ManHunter (06.11.2019 в 22:38):
Пока все как я и предполагал при тестировании. Отключение идет по максимальному значению. А если отправить 4 при поддерживаемом 5, то монитор уходит в коматоз.
Очень интересно посмотреть на значения ResS, CurS и MaxS на самсунговских мониторах.
zdm (06.11.2019 в 22:21):
2 шт. LG 24MP88HV
Погасли оба, не стенбай а полное отключение.
LG IPSFULLHD(HDMI) - ID: 0012h - Cur: 1 - Max: 4 - CurS: 0 - MaxS: 65535
LG IPSFULLHD(HDMI) - ID: 0012h - Cur: 1 - Max: 4 - CurS: 0 - MaxS: 65535
ManHunter (06.11.2019 в 21:52):
Александр, спасибо! Добавил проверку на выход MaximumValue за допустимые границы, благодарности proba. Обновил info.exe с прицелом на самсунговские VCP-коды.

На моем домашнем компе с дополнительным китайским монитором:
Philips 240B7QPT (24 inch Wide LCD MONITOR) - ID: 0009h - Cur: 1 - Max: 5 - ResS: 0 - CurS: 0 - MaxS: 0
Универсальный монитор PnP - ID: 0012h - Cur: 4 - Max: 4 - ResS: 0 - CurS: 0 - MaxS: 0

На ASUSовском ноуте после добавления проверки на предмет поддержки кода 0D6h - пустой файл, так и должно быть.
Александр (06.11.2019 в 21:22):
Выключается, лампочка не мигает, обратно включается с кнопки.
ManHunter (06.11.2019 в 20:44):
В коматоз с морганием лампочки или реально выключает так, что лампочка гаснет?
Александр (06.11.2019 в 20:42):
Работает
ManHunter (06.11.2019 в 19:51):
proba, это только для мониторов, которые именно мониторы. Для ноутов не катит.

Александр, это работает или не работает? Самсунг, как я понял из маркировки.
Александр (06.11.2019 в 19:50):
Info:SF350_S24F350FH / S24F352FH / S24F354FH (HDMI) - ID: 0012h - Cur: 1 - Max: 4
proba (06.11.2019 в 19:35):
>Если что-то будет работать не так, то...
Звучит как-то обнадёживающе. :-) Раз только было упомянуто, что поможет принудительное выключение питания, а на буке как?!
Info записал:
Универсальный монитор PnP - ID: 77a8387ah - Cur: 2007512178 - Max: 0

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2019
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0038 сек.) / Память: 4.75 Mb
Наверх