C++
Win32API
ConnectedStandby
InstantGo
ModernStandby

Connected Standby でスリープを検出する方法

※Win32の超絶原始的方法です。

WM_POWERBROADCASTをご存知ですか?

WM_POWERBROADCASTはConnected Standbyでは使えない

スリープとかサスペンドのことを調べてみると、いろんな方法が見つかります。System Power Management Eventsとか、SystemEvents.PowerModeChanged Eventとか。

WM_POWERBROADCASTは、電源まわりの何かが起きたときに発行されるウィンドウメッセージなので、例えば、ノートPCでAC電源を繋いだり外したりとかで受け取ることができます。
wParamに渡されるイベントの詳細を見ると、PBT_APMSUSPENDとあるので、スリープ検出できるような気がしてきますが、Connected StandbyとかInstant GoとかModern Standbyとか呼ばれているモードのWindowsで検出できません。

ちなみに、Win8以降でConnected Standby / Instant Go、Win10以降でModern Standbyと呼ばれているみたいです。だいたい同じものです。

PowerRegisterSuspendResumeNotification を使う

ひとつの解として、PowerRegisterSuspendResumeNotificationという関数があります。
スリープ・サスペンド・それらからの復帰のときに実行したい関数を、コールバック関数として登録することができます。

#include <Powerbase.h>
#pragma comment(lib, Powrprof.lib)

// コールバック関数を用意する
ULONG CALLBACK DeviceNotifyCallbackRoutine(
   _In_opt_ PVOID Context,
   ULONG          Type,
   PVOID          Setting
) {
  // ここにサスペンド・復帰したときの処理を書く
  if(Type == PBT_APMRESUMEAUTOMATIC) {
    // 例えばここはスリープやサスペンドから戻ってきたときに実行される
  }
}

int main() {

 // コールバック関数と、関数に渡すもの
  DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS parameters = { DeviceNotifyCallbackRoutine, &context };

  // コールバック関数を登録する. ユーザーモードでよいならRegisterSuspendResumeNotificationでもよい.
  HPOWERNOTIFY notify;
  PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, &parameters, &notify);

  // ...

  // 最後に登録を解除する.ユーザーモードの関数を使ったときはUnregisterSuspendResumeNotificationを使う.
 PowerUnregisterSuspendResumeNotification(notify);

}

個人的にstd::unique_ptrを使いたい衝動があるので、

// ポインタを解放するときの処理
struct SuspendResumeNotificationDeleter {
  using pointer = HPOWERNOTIFY;
  void operator ()(HPOWERNOTIFY notify) {
    PowerUnregisterSuspendResumeNotification(notify);
  }
};

// 使う時はこんな感じでいいのかなぁ
HPOWERNOTIFY notify;
DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS parameters = { DeviceNotifyCallbackRoutine, &context };
PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, &parameters, &notify);
std::unique_ptr<HPOWERNOTIFY, SuspendResumeNotificationDeleter> ptr(notify);
// 必要なくなったらptr.reset();

最後に

PowerModeChangedのほうが使いやすそう... Win32は苦行だし...