※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, ¶meters, ¬ify);
// ...
// 最後に登録を解除する.ユーザーモードの関数を使ったときは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, ¶meters, ¬ify);
std::unique_ptr<HPOWERNOTIFY, SuspendResumeNotificationDeleter> ptr(notify);
// 必要なくなったらptr.reset();
最後に
PowerModeChangedのほうが使いやすそう... Win32は苦行だし...