2

I have a headless computer running a custom service that I want to enable/disable using the power button, rather than having to remotely connect every time. The computer does other things as well, so turning it off is not an option.

Is it possible to hook the system power button under Windows XP & up, such that my program would get the event before Windows initiates a powerdown/sleep event (before PBT_APMQUERYSUSPEND gets sent out)?

I have figured this out, see my own answer below!

2 Answers 2

2

This is indeed possible, but it's a bit hackish and requires two completely different implementations depending on the Windows version. For both methods, you need to set your power button to put the computer to sleep in your power options.

Windows XP and below:

You need to overwrite your program's main window's WndProc function. On IDEs that don't support this natively, this can be done using SetWindowLong in user32 API. In your custom WndProc function, listen for a WM_POWERBROADCAST (0x218) message. If you receive a message with wParam of PBT_APMQUERYSUSPEND (0x0), call your wanted function and then return BROADCAST_QUERY_DENY (0x424D5144) instead of calling the base WndProc function. Example code:

//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 & up: (thanks to Remy Lebeau for setting me on the right track)

You need to override WndProc like for XP, but also call SetThreadExecutionState in kernel32 API to disable sleep mode and RegisterPowerSettingNotification in user32 API to listen for advanced power notifications. You will be listening, in particular, to the GUID_SYSTEM_AWAYMODE notification, which gets sent out when the system was asked to go to sleep but is unable to do so. To easily convert a string to a properly formed LPCGUID you can use UuidFromStringA in rpcrt4.dll API. Example code:

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)

This method has the side effect of turning off your physical screen (not a problem on a headless machine), and also possibly locking your session. Make sure you disable prompting for password after sleeping to avoid this. There's some additional useful information on RegisterPowerSettingNotification available here which shows how to extract information from the lParam in your WndProc function in case you want additional info on the notification. Have fun ;)

1

You can poll WMI for the shutdown event, but there is no way to stop it. So there is no gaurantee your program would receive the event before any services get terminated.

2
  • It looks like this would let me know about the shutdown event only AFTER Windows initiates shutdown. I'm looking for a way to find out about the power event before Windows initiates shutdown.
    – Duke Nukem
    Aug 23, 2013 at 2:43
  • Im fairly certain an ACPI shutdown command initiates a windows shutdown, so there isnt anything you would be able to do.
    – Keltari
    Aug 23, 2013 at 2:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .