るなおーびっと!

コマンドでスリープ状態(ハイブリッドスリープ)にする「正しい」方法とダメな方法がなぜダメなのか

2020/07/03 13:05 投稿

  • タグ:
  • Windows

いまだに誤った方法を紹介するサイトが多いので、なぜダメなのかに重きを置いて改めて記事を書くことにしました。

正しい方法は以前にも記事にしました。
【PowerShell】コマンドでスリープする方法

PowerShell -Command "Add-Type -Assembly System.Windows.Forms;[System.Windows.Forms.Application]::SetSuspendState('Suspend', $false, $false);"


誤った方法とは「rundll32.exe」を使用する方法です。

rundll32.exe PowrProf.dll,SetSuspendState

そもそもこれは何をしようとしているのかいうと、rundll32.exeを使ってPowerProf.dllの中にあるSetSuspendState関数を呼び出すというもの。
Windows APIには、Windowsをスリープ状態にするSetSuspendState関数があり、プログラムでこれを呼び出すことでそれが可能となります。
なので、rundll32.exeを使って直接SetSuspendState関数を呼び出せば、プログラミングすることなく簡単にスリープ状態にできるってのがこのコマンドの狙いです。間違いなんですけれどね。

なぜ上記が誤っているのかというと、そもそもSetSuspendState関数はrundll32.exeで呼び出すような使い方は想定されていないからなのです。
rundll32.exeは任意のWindows APIを呼び出せる実行ファイルではありません。

rundll32.exeで呼び出すことが想定されている関数の引数は無しか文字列1個です。
rundll32.exe dllファイル,関数名 引数(文字列のポインタ)

この引数に半角スペースを入れても、第二引数へ…とはならず、半角スペースも含めて一つの文字列として扱われて第一引数へ渡されます。

ここでSetSuspendState関数の定義を確認してみましょう。
SetSuspendState function

BOOLEAN SetSuspendState(
BOOLEAN bHibernate,
BOOLEAN bForce,
BOOLEAN bWakeupEventsDisabled
);

SetSuspendState関数の引数はBOOLEAN型3つです。どう見てもrundll32.exeでは正しく引数を渡すことができません。本当にありがとうございました。

さて、rundll32.exeでSetSuspendState関数を呼び出す方法を紹介しているサイトはそろって、休止状態を無効にしないと休止状態になることと、休止状態を無効にするとスリープ中に電源が切れるとメモリの内容が失われることと、タスクスケジューラを使ってスリープが解除できないことが書かれています。
それはなぜか、SetSuspendState関数の引数を見ていきましょう。

bHibernate: TRUEの場合休止状態、FALSEの場合サスペンド
bForce: TRUEやFALSEを渡しても変化なし。意味のないパラメータ
bWakeupEventDisabled: TRUEの場合スリープ解除イベントを無効にする

このことから、rundll32.exeで呼び出した場合は、おそらく全部TRUEとして扱われているようです。全部TRUEになるちゃんとした理由はあると思うが、ここでは割愛。

また、休止状態を無効にしてスリープすると、電源が切れたら作業中の内容は失われる原因は、Windows Vista 以降のスリープと呼ばれる機能は休止状態とサスペンド状態(Windows XP以前のスタンバイ状態に相当)を合わせたハイブリッドスリープ状態なので、休止状態を無効にすれば、ハイブリッドスリープの休止状態の性質が失われ、XP以前のようなただのスタンバイ状態になってしまいます。

また、以下のようなコマンドを紹介しているところもありますが…

rundll32.exe PowrProf.dll,SetSuspendState Sleep
rundll32.exe PowrProf.dll,SetSuspendState 0,1,0

先述の通り、この場合SetSuspendStateへ「Sleep」や「0,1,0」という文字列へのポインタを渡すことになるので、想定通りの動きはしません。


ここまでrundll32.exeで呼び出したらダメな理由を書きなぐりましたので、冒頭に述べた「PowerShell -Command "Add-Type -Assembly System.Windows.Forms;[System.Windows.Forms.Application]::SetSuspendState('Suspend', $false, $false);"」が正しい理由をば。

PowerShellは.NETのメソッドを呼ぶことができます。また.NETにはWindows APIのSetSuspendState関数とほぼ同じ動きをするApplication.SetSuspendStateメソッドがあります。
Application.SetSuspendState(PowerState, Boolean, Boolean) メソッド

上記のコマンドは「Add-Type -Assembly System.Windows.Forms;」でSystem.Windows.Formsをインポートして、「[System.Windows.Forms.Application]::SetSuspendState('Suspend', $false, $false);」でSetSuspendStateを呼び出しているということです。

パラメーター

state PowerState
移行後の電源の動作モードを示す PowerState。

force Boolean
すぐに、強制的に中断モードにする場合は true。Windows からすべてのアプリケーションに中断の要求が送られるようにする場合は false。

disableWakeEvent Boolean
システムの電源ステータスの復元が wake イベントでアクティブにならないようにするには true。システムの電源ステータスの復元が wake イベントでアクティブになるようにするには false。

PowerStateは列挙型。

フィールド

Hibernate 1
システムの電源モードが休止状態であることを示します。 システムが休止状態になると、コンピューターの電源がオフになる前に、メモリの内容がディスクに保存されます。 システムを再起動すると、デスクトップと以前に使用していたプログラムが復元されます。

Suspend 0
システムの電源モードがサスペンド状態であることを示します。 システムがサスペンド状態になると、コンピューターがスタンバイと呼ばれる低電力状態に切り替えられます。 コンピューターがスタンバイ モードになると、一部のデバイスの電源がオフになり、コンピューターが使用する電力が減少します。 ハイバネーションよりも短時間でシステムを復元できます。 スタンバイではメモリの状態がディスクに保存されないため、スタンバイ中に停電が発生すると、情報が失われることがあります。

先述のコマンドのSetSuspendState('Suspend', $false, $false)の部分はつまり、サスペンド状態、強制しない、ウェイクイベントを無効にしない、ということ。
ハイブリッドスリープが有効な環境であれば、サスペンドはハイブリッドスリープになります。
Windows APIのSetSuspendState関数のことを考えると、Application.SetSuspendStateも第二引数はおそらく意味のないパラメータ。

アプリを終了させるシャットダウンならともかく、アプリの状態をそのまま維持する休止やサスペンドにおいてアプリに何を強制させるのかって感じで意味がないんだと思われ。

ちなみに、単に休止状態にするだけならもっと簡単なコマンドがあります。

shutdown /h


コメント

コメントはまだありません
コメントを書き込むにはログインしてください。

いまブロマガで人気の記事