Это действительно возможно, но немного хакерски и требует двух совершенно разных реализаций в зависимости от версии 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-интерфейсе kernel32, чтобы отключить спящий режим, и RegisterPowerSettingNotification
в API-интерфейсе user32, чтобы прослушивать расширенные уведомления о питании. В частности, вы будете прослушивать уведомление GUID_SYSTEM_AWAYMODE
, которое отправляется, когда систему просят перейти в спящий режим, но не может этого сделать. Чтобы легко преобразовать строку в правильно сформированный LPCGUID
вы можете использовать UuidFromStringA
в API rpcrt4.dll. Пример кода:
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
на случай, если вам понадобится дополнительная информация об уведомлении. Повеселись ;)