我该如何知道Windows何时进入/退出睡眠或休眠模式?

27

有没有可能订阅一个 Windows 事件,在 Windows 进入或退出睡眠或休眠状态时触发?

我需要让我的应用程序在计算机进入睡眠状态时得到通知以便进行一些清理工作,并避免它从睡眠状态中恢复时出现时间问题。


对于新读者 - 请注意,有一个新的S0电源模式状态(现代待机),所有在此处提出的解决方案都不适用: https://learn.microsoft.com/en-us/windows/desktop/power/system-power-states#sleep-state-modern-standby (一旦我得到答案,我会更新回答) - Oded Ben Dov
6个回答

22

7

4
在一个Visual Studio 2005的C++ MFC应用程序中,您需要将ON_MESSAGE()添加到消息映射中,查找WM_POWERBROADCAST消息,如下例所示:
BEGIN_MESSAGE_MAP(CFrameworkWndDoc, CWindowDocument)
    //{{AFX_MSG_MAP(CFrameworkWndDoc)
    ON_WM_CHAR()
    ON_WM_TIMER()
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_POWERBROADCAST, OnPowerMsgRcvd)
END_MESSAGE_MAP()

接下来,您需要添加消息处理程序函数以及类定义更改,以声明消息处理程序的成员函数,以便您可以检查wParam变量以获取消息类型,如以下框架所示:

// Handle the WM_POWERBROADCAST message to process a message concerning power management
// such as going to Sleep or Waking Up.
LRESULT CFrameworkWndDoc::OnPowerMsgRcvd(WPARAM wParam, LPARAM lParam)
{
    switch (wParam) {
        case PBT_APMPOWERSTATUSCHANGE:
            TRACE0("PBT_APMPOWERSTATUSCHANGE  received\n");
            break;
        case PBT_APMRESUMEAUTOMATIC:
            TRACE0("PBT_APMRESUMEAUTOMATIC  received\n");
            break;
        case PBT_APMRESUMESUSPEND:
            TRACE0("PBT_APMRESUMESUSPEND  received\n");
            break;
        case PBT_APMSUSPEND:
            TRACE0("PBT_APMSUSPEND  received\n");
            break;
    }

    return 0;
}

我所看到的是,在运行于 Windows 7 上的应用程序中使用上述测试,当我在调试器中启动应用程序并手动使我的电脑进入睡眠状态时,我会看到以下消息:
PBT_APMSUSPEND  received

当计算机重新启动并且我登录后,在调试器输出窗口中会看到两条消息,依次显示:
PBT_APMRESUMESUSPEND  received
PBT_APMRESUMEAUTOMATIC  received

我所找到的一切信息都表明,您无法确定您是从睡眠状态还是休眠状态中恢复过来。目前,我仍在进一步研究挂起或恢复时需要处理哪些文件和设备句柄。我看到有迹象表明,在恢复后,COM端口的文件句柄将不再有效。此外,我也不确定与其他进程的接口,例如数据库连接。
除了标准的睡眠和休眠电源管理状态外,Microsoft还推出了Windows 8和8.1中的已连接待机电源状态,这取决于应用程序类型,可能会对应用程序设计产生影响。
桌面应用程序通常无需额外工作即可与连接的待机状态集成。 桌面活动调节器 (DAM) 是 Windows 组件,它在连接的待机状态下暂停所有桌面应用程序并限制第三方系统服务的运行时间。 DAM 的目的是维护与现有应用程序和服务的基本软件兼容性,但减少它们在睡眠期间对电池寿命的影响。 Windows 在 DAM 阶段完成后阻止桌面应用程序在任何连接待机状态的部分运行。 Windows 允许第三方系统服务在完成 DAM 阶段后以受限模式执行。 在此模式下,第三方服务每 30 秒最多可以运行一秒钟的墙时时间。

Intel的Lynn Merrill所写的《优雅应用程序暂停的艺术》提供了有关处理Windows下与电源管理相关的各种Windows消息类型的信息,但该文档的日期为2005年,因此并非所有材料都适用于Windows XP之后的版本。在描述此文档中的消息序列时,至少有一条不再使用的消息是PBT_APMQUERYSUSPEND消息,该消息在Windows Vista之后不再被Windows使用,它曾用于请求应用程序是否能够暂停。现在,SetThreadExecutionState()函数用于指示线程不能被中断以更改为睡眠或休眠状态。有关电源管理状态消息更改的详细信息,请参见stackoverflow上的答案Can't catch sleep suspend messages (winxp)

自Windows XP以来的系统电源状态事件

自Windows XP以来,Microsoft已经改进了电源管理,因为Windows操作系统的各个版本共享更多的组件,而且现在部署在需要更仔细的电源管理的电池供电的小型设备上。请参见Registering for Power Events。有一个RegisterPowerSettingNotification()函数和一个UnregisterPowerSettingNotification()函数。

一个应用程序或服务使用RegisterPowerSettingNotification函数注册通知。当相应的电源设置更改时,系统会发送通知,具体如下:
  • 应用程序接收到一个WM_POWERBROADCAST消息,其中wParamPBT_POWERSETTINGCHANGElParam指向一个POWERBROADCAST_SETTING结构。
  • 服务通过调用RegisterServiceCtrlHandlerEx函数注册了HandlerEx回调函数,因此会接收到一次调用。发送到HandlerEx回调函数的lpEventData参数指向一个POWERBROADCAST_SETTING结构。
POWERBROADCAST_SETTING结构中,PowerSetting成员包含标识通知的GUID,而Data成员包含电源设置的新值。
另请参见Power Management Functions,了解自Windows Vista以来Windows API中的电源管理功能列表。

系统电源状态和电池状况

您可以使用Windows系统服务API函数GetSystemPowerStatus()来检索当前的电源状态。

检索系统的电源状态。该状态指示系统是否正在使用交流或直流电源,电池当前是否正在充电,剩余多少电池寿命以及电池节能模式开启或关闭。

但是请注意,在SYSTEM_POWER_STATUS结构中返回的信息关于电池状态和交流/直流电源来源,而不是实际设备的电源状态,例如睡眠或休眠

如果Windows设备处于睡眠或休眠状态,则您的应用程序将无法运行,因此无法使用此函数确定当前的Windows电源状态。我在这里包括这个注意事项,因为它可能对正在研究此主题的人有用。


+1 不知道关于连接待机,一直在想为什么我的Windows服务在平板电脑上表现不良。非常感谢! - ur.

4

请问您是否有一个可用的代码示例?(我尝试了一些在线示例,但它们似乎无法触发事件) - Yoav Feuerstein

4

不确定您需要多频繁地监控此项内容,但如果您在.NET中编写一个服务,可以重写ServiceBase,将CanHandlePowerEvent设置为true,然后通过PowerBroadcastStatus枚举通知您电源变化的情况。


谢谢您提到了“CanHandlePowerEvent”,那正是我的问题所在! - n0ne

-1

您可以订阅NetworkChange.NetworkAvailabilityChanged和NetworkChange.NetworkAddressChanged。

通常我会启动一个两秒的计时器,以便在超时后从睡眠模式中恢复网络通信。


这并没有提供对所问问题的答案。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接