mogmo .NET

C#/XAML/VB6たまにC++についてメモ程度に書いていく。あくまで自分用。責任は取れません。

WinAPI|SetThreadExecutionStateを使ってスリープなどへの移行を防ぐ

WinAPIを使って、アプリ起動中に

への移行を抑止する方法について、調べて実験した内容を書いていきます。

SetThreadExecutionState API

Win32APIのSetThreadExecutionStateAPIをコールすることで、スリープモードなどの移行を抑止できます。

何をどのように抑止するかは引数で決めることができます。

// スタンバイ移行までのタイマーをリセットする // caution: 数十秒に1回ほど,繰り返し呼び出す必要がある
::SetThreadExecutionState(ES_SYSTEM_REQUIRED);
// スタンバイを抑止 // memo: AppのInitInstanceなどで1度呼べばOK
::SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS);

// ディスプレイ自動OFFやスクリーンセーバーの突入までのタイマーをリセットする // caution: 数十秒に1回ほど,繰り返し呼び出す必要がある
::SetThreadExecutionState(ES_DISPLAY_REQUIRED);
// ディスプレイ自動OFFやスクリーンセーバーの突入を抑止 // memo: AppのInitInstanceなどで1度呼べばOK
::SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS);

// 抑止を解除
::SetThreadExecutionState(ES_CONTINUOUS);

docs.microsoft.com

引数

引数 説明 使いどころ
ES_SYSTEM_REQUIRED システムがシステムアイドルタイマをリセットして、コンピュータをスリープ状態にするのを防ぐことができる。 処理中などスリープしてほしくない時
ES_DISPLAY_REQUIRED ディスプレイのアイドルタイマーをリセットして、ディスプレイを強制的にオンにします。 ビデオ再生など,長時間ユーザー操作がない状態
ES_CONTINUOUS 次にES_CONTINUOUSを呼び出すまで,実行を維持する必要があることをシステムに通知する。 1回の呼び出しで済ませたいとき,抑制を解除するとき

戻り値

成功 前のスレッド実行状態を返す
失敗 NULL

タイマーを使う場合

Windowsの設定の最小単位が1分なので,タイマー間隔は30秒程度にしておけば問題ないと思われる。
ただし,APIを使えば細かい1分以下の設定もできるようなので,要件によってポーリング間隔を調整すると良い。
f:id:mogmo811:20201228190453j:plain
f:id:mogmo811:20201228190519j:plain

注意事項

  • 自動的な突入の回避は可能だが,ユーザー操作によるスリープは検知不可能
  • このAPIが効かないPCがある
  • アプリ終了時,抑止するアプリがいなくなったことで自動的にスリープ抑止の解除が行われるが,明示的に抑止解除のコードを明記しておいた方がお行儀がいい
  • ES_DISPLAY_REQUIREDは|ES_CONTINUOUSとセットで使えないとの記事が多く見受けられたが,私の環境では正常に動いた。理由は調査中。

テストプログラムで実験

電源要求を確認

アプリケーションとドライバーの電源要求がないか,コマンドプロンプト(管理者権限)で以下のコマンドを打って確認します。

powercfg /requests

電源要求があると、スリープやディスプレイの自動電源OFFが働きませんので,"ない"状態で試しましょう。

スリープ,スクリーンセーバー,ディスプレイの自動電源OFFを確認

Windowsの設定を調整して,まずは設定がちゃんと働くか確認

テストプログラムを実行

簡単なテストプログラムを作り,自動突入を防げるか確認。
コードはGitHubに載せたので割愛します。
github.com

結果

以下のテストを行い,すべてパスした。

SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS)をコールしてシステムスリープ移行の抑止 OK
SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS)をコールしてディスプレイの自動電源OFF移行の抑止 OK
SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS)をコールしてスクリーンセーバー移行の抑止 OK
SetThreadExecutionState(ES_SYSTEM_REQUIRED)をTimerで30秒ごとにコールしてシステムスリープ移行の抑止 OK
SetThreadExecutionState(ES_DISPLAY_REQUIRED)をTimerで30秒ごとにコールしてディスプレイの自動電源OFFの抑止 OK
SetThreadExecutionState(ES_DISPLAY_REQUIRED)をTimerで30秒ごとにコールしてスクリーンセーバー移行の抑止 OK
SetThreadExecutionState(ES_CONTINUOUS)をコールして抑止の解除→Windowsの設定通りの動作をする OK

ではまた。
written by @mogmo1012