You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测Windows休眠唤醒后Qt程序恢复运行并处理事件

Qt检测Windows休眠唤醒事件的实现方案

一、通过QWidget::nativeEvent实现事件监听

核心步骤

  1. 注册正确的电源通知:使用RegisterPowerSettingNotification注册GUID_SYSTEM_POWER_STATE(对应系统电源状态变化,而非显示器状态),需在窗口构造时获取HWND并完成注册。
  2. 重写nativeEvent处理消息:拦截Windows原生的WM_POWERBROADCAST消息,根据不同参数判断休眠/唤醒事件。

代码示例

#include <QMainWindow>
#include <windows.h>
#include <powrprof.h>
#pragma comment(lib, "powrprof.lib")

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        HWND hwnd = reinterpret_cast<HWND>(winId());
        // 注册系统电源状态变化通知
        RegisterPowerSettingNotification(hwnd, &GUID_SYSTEM_POWER_STATE, DEVICE_NOTIFY_WINDOW_HANDLE);
    }

protected:
    bool nativeEvent(const QByteArray &eventType, void *message, long *result) override
    {
        MSG* msg = static_cast<MSG*>(message);
        if (msg->message == WM_POWERBROADCAST)
        {
            switch (msg->wParam)
            {
                case PBT_APMSUSPEND:
                    // 系统即将进入休眠,执行自定义处理逻辑
                    break;
                case PBT_APMRESUMEAUTOMATIC:
                    // 系统自动唤醒(如定时任务触发)
                    break;
                case PBT_APMRESUMESUSPEND:
                    // 用户手动唤醒系统
                    break;
                case PBT_POWERSETTINGCHANGE:
                    // 获取更详细的电源状态信息
                    POWERBROADCAST_SETTING* setting = reinterpret_cast<POWERBROADCAST_SETTING*>(msg->lParam);
                    if (IsEqualGUID(setting->PowerSetting, GUID_SYSTEM_POWER_STATE))
                    {
                        DWORD powerState = *reinterpret_cast<DWORD*>(setting->Data);
                        // powerState对应不同电源状态,如0=工作状态,1=休眠等
                    }
                    break;
            }
        }
        return QMainWindow::nativeEvent(eventType, message, result);
    }
};

注意点

不要使用GUID_SESSION_DISPLAY_STATUS,该GUID仅用于监听显示器的开启/关闭/变暗,与系统休眠唤醒无关。


二、nativeEvent与QCoreApplication::installNativeEventFilter的选择

  • 单个窗口需求:优先用nativeEvent,代码仅绑定目标窗口,耦合度低,实现简单。
  • 全局应用需求:如果多个窗口或整个应用都需要监听,使用installNativeEventFilter更高效,只需注册一次全局过滤器,即可处理所有窗口的原生事件,避免重复代码。
  • 两者性能无显著差异,均直接处理Windows原生消息,选择取决于应用架构。

三、使用PowerRegisterSuspendResumeNotification的方案

这是Windows Vista及以上版本提供的专用API,专门用于监听休眠/唤醒事件,无需拦截窗口消息。

代码示例

  1. 定义回调函数
#include <QDebug>
#include <windows.h>
#include <powrprof.h>
#pragma comment(lib, "powrprof.lib")

VOID CALLBACK PowerSuspendResumeCallback(PVOID Context, ULONG Type, PVOID Setting)
{
    switch (Type)
    {
        case PBT_APMSUSPEND:
            qDebug() << "系统进入休眠";
            // 若需执行Qt UI操作,需切换到主线程
            // QMetaObject::invokeMethod(qApp, [](){ /* UI操作 */ }, Qt::QueuedConnection);
            break;
        case PBT_APMRESUMEAUTOMATIC:
            qDebug() << "系统自动唤醒";
            break;
        case PBT_APMRESUMESUSPEND:
            qDebug() << "用户唤醒系统";
            break;
    }
}
  1. 注册与注销通知
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    REGISTER_NOTIFICATION_REQUEST req{};
    req.Version = REGISTER_NOTIFICATION_REQUEST_VERSION;
    req.Type = PowerRegisterSuspendResume;
    req.SuspendResumeCallback = PowerSuspendResumeCallback;
    req.Context = nullptr; // 可传递自定义上下文数据

    HPOWERNOTIFY hNotify;
    PowerRegisterSuspendResumeNotification(&req, &hNotify);

    MainWindow w;
    w.show();

    int ret = a.exec();

    // 程序退出前注销通知
    PowerUnregisterSuspendResumeNotification(hNotify);
    return ret;
}

回调执行说明

  • 回调函数运行在系统线程,而非Qt主线程,因此如果要执行UI操作或Qt相关逻辑,必须通过QMetaObject::invokeMethod切换到主线程,避免线程安全问题。
  • 该API是异步触发的,注册后不会阻塞主线程,系统会在事件发生时主动调用回调。

内容的提问来源于stack exchange,提问作者riverofwind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:16:03