「バッテリー節約(~24H1)」と「省電力(24H2~)」と、それらに入ったことの検知

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

Windows10のときには、WM_POWERBROADCASTメッセージのPBT_POWERSETTINGCHANGEの中のPowerSettingがGUID_POWER_SAVING_STATUSのときのDataをみて、0だったらバッテリー節約を抜けた、0以外だったらバッテリー節約に入った、ということを取れていたが、どうも24H2以降ではそれがとれなくなってるっぽい。

なんでか?調べる。

「Battery Saver」と「Energy Saver」

learn.microsoft.com

learn.microsoft.com

上のドキュメントによると、どうも、
24H1以前はBattery Saver(日本語で言うとバッテリー節約)と言っていたWindowsの機能が、
24H2以降ではEnergy Saver(日本語で言うと省電力)機能になったらしい。

※「バッテリー節約」と「省電力」の日本語は上のページの「ja-jp」版より。

自宅PCがノートPCでないのでしっかり試せてないが、確かどちらもタスクトレイのアイコンで、「電池マークに葉っぱマークがつく」みたいな見え方をしてたはず。

なので、ぱっと見、24H2の「省電力」への移行と離脱も同じコードで取れるかと思ったら、同じコードではダメっぽい。

どうすれば取れるかを調べる。

「バッテリー節約」と「省電力」で使うGUID

下記のドキュメントに、節約に入る時、出るときを見るためのGUIDが載っている。

https://learn.microsoft.com/ja-jp/windows/win32/power/power-setting-guids

GUID_POWER_SAVING_STATUS のほうが、windows10のときに、バッテリー節約のIN/OUTを見るために使っていたGUID。

その下に、GUID_ENERGY_SAVER_STATUSという、見るからに「省電力」のために使いそうなGUIDがあった。

これが、24H2以降の「省電力」へのIN/OUTを見るためのGUIDっぽい。

試してみる。

試したコードでエラーが出た

GUID_ENERGY_SAVER_STATUS を使ったコードを書くと、下記のようなエラーが出た。

おそらく、自宅PCに、24H2の開発環境が入っていないからだと思われる。
(あとVisualStudioが古いっぽい)

■今の環境

10.0.22621.0(22H2)までしかない。

アップデートしてみる。

■アップデート後の環境

上記VSにアップデートして、VSInstallerで10.0.26100.0(これが24H2なのかな?)をインストールし、

C++プロジェクトのプロパティの「WindowsSDKバージョン」で「10.0.26100.0」を選択すると、エラーは消えた。

とれそうなコード

※自宅PCがノートでないので試せてない。が、MSのドキュメントからすると、下記のコードで動きそうな気がする。 動かせる環境手に入れ次第、試してみる。

#include <iostream>
#include <chrono>
#include "framework.h"
#include "PowerSaverJikken.h"
#include "resource.h"

HINSTANCE hInst;

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    hInst = hInstance;
    DialogBox(hInst, L"MyTestDlgBase_Main", NULL, (DLGPROC)MyDlgProc);
    return (int)0;
}

std::wstring getTimeStamp()
{
    auto now = std::chrono::system_clock::now();
    auto now_c = std::chrono::system_clock::to_time_t(now);
    
    tm time;
    auto err = localtime_s(&time, &now_c);
    auto timeObj = std::put_time(&time, L"%Y/%m/%d %H:%M:%S");
    
    std::wostringstream ss;
    ss << timeObj;
    return ss.str();
}

void AddMsg(HWND hDlg, std::wstring msg)
{
    auto list = GetDlgItem(hDlg, IDC_LIST1);
    auto msgWithTime = getTimeStamp() + L" " +  msg;
    SendMessage(list, LB_INSERTSTRING, (WPARAM)0, (LPARAM)msgWithTime.c_str());
}

BOOL CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_INITDIALOG:
        {
            RegisterPowerSettingNotification(hDlg, &GUID_POWER_SAVING_STATUS, DEVICE_NOTIFY_WINDOW_HANDLE);
            RegisterPowerSettingNotification(hDlg, &GUID_ENERGY_SAVER_STATUS, DEVICE_NOTIFY_WINDOW_HANDLE);
            break;
        }

        case WM_POWERBROADCAST:
        {
            if (wp == PBT_POWERSETTINGCHANGE)
            {
                auto lppbc = (POWERBROADCAST_SETTING*)lp;

                // 24H1までの「バッテリー節約」
                if (lppbc->PowerSetting == GUID_POWER_SAVING_STATUS) {
                    if (lppbc->Data[0] == 0)
                        AddMsg(hDlg, L"Power Saving OFF(GUID_POWER_SAVING_STATUS)");
                    else
                        AddMsg(hDlg, L"Power Saving ON(GUID_POWER_SAVING_STATUS)");
                }

                // 24H2以降の「省電力」
                if (lppbc->PowerSetting == GUID_ENERGY_SAVER_STATUS) {
                    if      (lppbc->Data[0] == ENERGY_SAVER_OFF)
                        AddMsg(hDlg, L" ENERGY_SAVER_OFF");
                    else if (lppbc->Data[0] == ENERGY_SAVER_STANDARD)
                        AddMsg(hDlg, L" ENERGY_SAVER_STANDARD");
                    else if (lppbc->Data[0] == ENERGY_SAVER_HIGH_SAVINGS)
                        AddMsg(hDlg, L" ENERGY_SAVER_HIGH_SAVINGS");
                    else
                        AddMsg(hDlg, L" ???");
                }
            }
            break;
        }
    }
    return FALSE;
}

24/09/12 追記

試した結果、このコードで、「バッテリー節約」も「省電力」も、取ることができた。

但し、いくつか注意しないと行けないなと思った点があった。

上のコードを使うときの注意

  • 2024/9月時点のwindows11 24H2で試した時点では、GUID_POWER_SAVING_STATUSGUID_ENERGY_SAVER_STATUSも、両方を通っていた。
    なので、24H1以前向けのつもりでGUID_POWER_SAVING_STATUS、24H2以降向けのつもりでGUID_ENERGY_SAVER_STATUS、と両方を同時に書いて、 両方に「葉っぱを開始したとき」にやりたい処理を書いたりしていると、意図せず二重に処理を実施してしまう、とかになりそう。
  • GUID_ENERGY_SAVER_STATUSのほうは24H2時点のWIndowsの節約のためのUIの通りに(節約の閾値をバッテリ残量が下回ったら節約に入る、とか)動いてそうだが、GUID_POWER_SAVING_STATUSのほうは、どういう基準で「節約」に入ってるのか不明。(なにか、閾値があるのか??)

省電力の説明、何気に怖いことを書いてないか...

バックグラウンドアプリをブロックしますとか、一部タスクスケジューラを動かさないとかかれている。

何気にそういう機能を利用している場合に、アプリ動かない、とかならないか。。。。

https://learn.microsoft.com/ja-jp/windows-hardware/design/component-guidelines/energy-saver

参考

24H1以前の「バッテリー節約」と24H2以降の「省電力」について

バッテリー節約(旧)

https://learn.microsoft.com/ja-jp/windows-hardware/design/component-guidelines/battery-saver

省電力(新)

https://learn.microsoft.com/ja-jp/windows-hardware/design/component-guidelines/energy-saver

電源設定GUIDの一覧

https://learn.microsoft.com/ja-jp/windows/win32/power/power-setting-guids