1

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 event get sent out)?

4

1 回答 1

3

这确实是可能的,但它有点骇人听闻,并且需要两个完全不同的实现,具体取决于 Windows 版本。对于这两种方法,您都需要在电源选项中设置电源按钮以使计算机进入睡眠状态。

Windows XP 及以下:

您需要覆盖程序主窗口的WndProc功能。在本机不支持此功能的 IDE 上,可以使用SetWindowLonguser32 API 来完成。在您的自定义WndProc函数中,收听一条WM_POWERBROADCAST (0x218)消息。如果您收到 wParam of 的消息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 及更高版本:( 感谢 Remy Lebeau 让我走上正轨)

您需要WndProc像 XP 一样覆盖,但还需要调用SetThreadExecutionStatekernel32 API 来禁用睡眠模式,并调用RegisterPowerSettingNotificationuser32 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提供了一些其他有用的信息,显示了如何从函数中提取信息,以防您需要有关通知的其他信息。玩得开心 ;)lParamWndProc

于 2013-08-27T06:26:42.537 回答