在 Windows 中挂钩系统电源按钮 [英] Hook system power button in Windows

查看:36
本文介绍了在 Windows 中挂钩系统电源按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一台运行自定义服务的无头计算机,我想使用电源按钮启用/禁用该服务,而不必每次都进行远程连接.计算机也可以做其他事情,所以关闭它不是一种选择.

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.

Windows XP & 下可以挂系统电源键吗?启动,这样我的程序将在 之前 Windows 启动掉电/睡眠事件(在 PBT_APMQUERYSUSPEND 事件被发送之前)获得事件?

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)?

推荐答案

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

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 及以下:

您需要覆盖程序主窗口的WndProc 函数.在本机不支持此功能的 IDE 上,可以使用 user32 API 中的 SetWindowLong 来完成.在您的自定义 WndProc 函数中,监听 WM_POWERBROADCAST (0x218) 消息.如果您收到带有 PBT_APMQUERYSUSPEND (0x0) 的 wParam 的消息,请调用您想要的函数,然后返回 BROADCAST_QUERY_DENY (0x424D5144) 而不是调用基本的 WndProc> 功能.示例代码:

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: (感谢 Remy Lebeau 让我走上正轨)

您需要像 XP 一样覆盖 WndProc,还需要在 kernel32 API 中调用 SetThreadExecutionState 以禁用睡眠模式,并在 user32 API 中调用 RegisterPowerSettingNotification 以侦听高级电源通知.您将特别聆听 GUID_SYSTEM_AWAYMODE 通知,该通知在系统被要求进入睡眠状态但无法这样做时发出.要轻松地将字符串转换为正确格式的 LPCGUID,您可以在 rpcrt4.dll API 中使用 UuidFromStringA.示例代码:

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)

此方法具有关闭物理屏幕的副作用(在无头机器上不是问题),并且还可能锁定您的会话.确保在睡眠后禁用密码提示以避免这种情况.在此处RegisterPowerSettingNotification 的其他有用信息> 它显示了如何从 WndProc 函数中的 lParam 中提取信息,以防您需要有关通知的其他信息.玩得开心;)

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 ;)

这篇关于在 Windows 中挂钩系统电源按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆