Крючок кнопки включения системы в Windows

У меня есть безголовый компьютер, на котором запущена специальная служба, которую я хочу включить / отключить с помощью кнопки питания, а не удаленного подключения каждый раз. Компьютер выполняет и другие функции, поэтому отключать его нельзя.

Можно ли подключить кнопку питания системы под Windows XP и выше, чтобы моя программа получала событие до того, как Windows инициирует событие отключения питания (до PBT_APMQUERYSUSPEND будет отправлен)?

Я понял это, см. Мой собственный ответ ниже!

2 ответа

Решение

Это действительно возможно, но немного хакерски и требует двух совершенно разных реализаций в зависимости от версии Windows. Для обоих способов вам нужно установить кнопку питания, чтобы перевести компьютер в режим сна.

Windows XP и ниже:

Вам нужно переписать главное окно вашей программы WndProc функция. В IDE, которые не поддерживают это изначально, это можно сделать с помощью SetWindowLong в API user32. В вашем обычае WndProc функция, слушай WM_POWERBROADCAST (0x218) сообщение. Если вы получаете сообщение с wParam из PBT_APMQUERYSUSPEND (0x0) Вызовите нужную функцию, а затем вернитесь BROADCAST_QUERY_DENY (0x424D5144) вместо вызова базы WndProc функция. Пример кода:

//At program start
//GWL_WNDPROC = -4
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)

//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_APMQUERYSUSPEND = 0x0
//BROADCAST_QUERY_DENY = 0x424D5144
if wMsg = WM_POWERBROADCAST && wParam = PBT_APMQUERYSUSPEND (
    //CALL YOUR FUNCTION HERE!
    return BROADCAST_QUERY_DENY
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)

//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)

Windows Vista и более поздние версии : (спасибо Реми Лебо за правильную настройку)

Вы должны переопределить WndProc нравится для XP, но и звоните SetThreadExecutionState в API ядра32, чтобы отключить спящий режим и RegisterPowerSettingNotification в user32 API для прослушивания расширенных уведомлений о питании. Вы будете слушать, в частности, GUID_SYSTEM_AWAYMODE уведомление, которое отправляется, когда системе было предложено перейти в спящий режим, но не может этого сделать. Чтобы легко преобразовать строку в правильно сформированную LPCGUID ты можешь использовать UuidFromStringA в rpcrt4.dll API. Пример кода:

typedef struct UUID{
    int d1, d2, d3, d4
} LPCGUID;

//At program start
//GWL_WNDPROC = -4
//ES_CONTINUOUS = 0x80000000
//ES_SYSTEM_REQUIRED = 0x1
//ES_AWAYMODE_REQUIRED = 0x40
//GUID_SYSTEM_AWAYMODE = "98a7f580-01f7-48aa-9c0f-44352c29e5C0"
LPCGUID uid;
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED)
UuidFromStringA(*(GUID_SYSTEM_AWAYMODE), uid)
ps = RegisterPowerSettingNotification(this.hWnd, uid, 0)

//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_POWERSETTINGCHANGE = 0x8013
if wMsg = WM_POWERBROADCAST && wParam = PBT_POWERSETTINGCHANGE (
    //CALL YOUR FUNCTION HERE!
    //You can additionally extract data from the lParam to verify
    //this is the notification you're waiting for (see below)
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)

//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)
UnregisterPowerSettingNotification(ps)

Этот метод имеет побочный эффект: отключение физического экрана (это не проблема для безголовой машины), а также, возможно, блокировка сеанса. Убедитесь, что вы отключили запрос пароля после сна, чтобы избежать этого. Там есть дополнительная полезная информация о RegisterPowerSettingNotification доступно здесь, который показывает, как извлечь информацию из lParam в вашем WndProc Функция, если вы хотите получить дополнительную информацию в уведомлении. Повеселись;)

Вы можете опросить WMI для события завершения работы, но нет способа остановить его. Таким образом, нет гарантии, что ваша программа получит событие до того, как какие-либо услуги будут прекращены.

Другие вопросы по тегам