Крючок кнопки включения системы в 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 для события завершения работы, но нет способа остановить его. Таким образом, нет гарантии, что ваша программа получит событие до того, как какие-либо услуги будут прекращены.