Pull to refresh

Выключаем монитор программно в Windows

На хабре есть статья как сделать это из Linux: ссылка Полезная вещь, но не все используют Linux, поэтому я решил написать что-то подобное для Windows. В Windows есть несколько способов добраться до I2C шины монитора, но самый простой из них появился в Windows Vista, его и будем использовать.

Ссылка на соответствующий раздел MSDN: ссылка
Итак, алгоритм управления питанием всеми подключёнными мониторами:
  • Перебираем все логические мониторы в системе, вызывая функцию EnumDisplayMonitors, в ней указываем свою callback функцию которая будет получать описатель для каждого логического монитора – HMONITOR
  • Зная HMONITOR-описатель монитора получаем соответствующий описатель физического монитора — HANDLE, для этого используем GetPhysicalMonitorsFromHMONITOR. Один логический монитор в Windows может включать в себя несколько физических мониторов, поэтому потребуется еще функция GetNumberOfPhysicalMonitorsFromHMONITOR
  • Найденные неповторяющиеся описатели физических мониторов сохраняем в отдельном списке
  • Посылаем команду мониторам, используя функцию SetVCPFeature

К сожалению, есть два неприятных нюанса. Во-первых, всё это будет работать только в Windows Vista или Windows 7. А во-вторых, и это самое неприятное, всё это будет работать не со всеми мониторами, так как некоторые производители мониторов не слишком заботятся о корректной поддержке стандарта DDC-CI. А бывает вообще про него забывают, а только поддерживают чтение EDID, чтобы Windows знала какие разрешения поддерживает монитор. Samsung же вообще использует свой собственный DDC-CI код для управления питанием.
Тем не менее, на большинстве современных мониторов данный подход будет работать.

Код консольной утилиты получился совсем небольшим:

<code>
// код команды управлением питания 0xD6
//константы для этой команды:
#define POWER_ON                         0x01 
#define POWER_STANDBY                0x02
#define POWER_SUSPEND               0x03
#define POWER_OFF                        0x04

//список для описателей физических мониторов
std::vector<HANDLE> MonHandles;

void AddToMonHandles(HANDLE h)
{
	int i, cnt;
	cnt = MonHandles.size();
	for (i = 0; i < cnt; i++)	
		if (MonHandles[i] == h) return;
	MonHandles.push_back(h);
}

//callback функция для EnumDisplayMonitors
BOOL CALLBACK EnumProc(
  HMONITOR hMonitor,  // описатель логического монитора
  HDC hdcMonitor,     
  LPRECT lprcMonitor, 
  LPARAM dwData       
)
{
	LPPHYSICAL_MONITOR pMons = NULL;
	DWORD i, mcnt;
	if (!GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, &mcnt)) return TRUE;
    pMons = (LPPHYSICAL_MONITOR)malloc(mcnt * sizeof(PHYSICAL_MONITOR));
	if (GetPhysicalMonitorsFromHMONITOR(hMonitor, mcnt, pMons))
		for (i = 0; i < mcnt; i++)
			AddToMonHandles(pMons[i].hPhysicalMonitor);
	free(pMons);
	return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc < 2) return 0;
	bool IsOn = _wcsicmp(argv[1], L"-off") != 0;

	EnumDisplayMonitors(NULL, NULL, EnumProc, NULL);
	int i, cnt;
	cnt = MonHandles.size();
	for (i = 0; i < cnt; i++)
	{
		//посылаем команду управления питанием
		SetVCPFeature(MonHandles[i], 0xD6, IsOn ? POWER_ON : POWER_OFF);
		//а это специально для мониторов Samsung
		SetVCPFeature(MonHandles[i], 0xE1, IsOn ? 1 : 0);
		//разобравшись, что работает на вашем мониторе, одну из строчек можно удалить
	}
	return 0;
}
</code>

Параметр «-off» выключает все подключённые мониторы, любой другой параметр их включает. Исходный код для VS2008 и готовый EXE можно скачать тут: ссылка
Возможно в будущем дойдут руки сделать полноценную утилиту, которая запускается при старте Windows и включает мониторы, а при завершении работы – выключает, и/или управляет питанием по горячей клавише – тут много можно чего придумать.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.