PowrProf.dll と shutdown.exe を呼んで
サスペンド、ハイバネート、シャットダウン、リブート、ログオフを
n 秒後に実行できるようにしてみました。
サスペンド後にタスクスケジューラでの復帰もできます。(後述)
テスト環境は winXP SP3、Activeperl 5.14。
#使用例 #t オプションのデフォルト値は 30秒 #カウントダウン中、CTRL+C で中止します #60秒後にサスペンド > pwrctrl.pl -s -t 60 > pwrctrl.pl --suspend --timeout 60 #即シャットダウン > pwrctrl.pl -d -t 0 > pwrctrl.pl --shutdown --timeout 0 #ヘルプ > pwrctrl.pl -? > pwrctrl.pl --help
# pwrctrl.pl # windows power control script use strict; use warnings; use Getopt::Long; use Win32::API; our $VERSION = "0.02"; $| = 1; #no buffering $Win32::API::DEBUG = 0; #Win32::API debug flag my $cmd = init(); count_down($cmd); my $return_code = $cmd->{'run'}->(); print "return code : $return_code\n"; exit; #-------------------- sub init{ usage() unless @ARGV; ###get options my $opts = {'timeout' => 30}; #default GetOptions($opts, 'help|?', 'hibernate|h', 'suspend|s', 'shutdown|d', 'reboot', 'logoff', 'timeout=i', ) || usage(); usage() if $opts->{'help'}; ###set command my $api = Win32::API->new('Powrprof', 'SetSuspendState', 'NNN', 'N') || die "Can't load API : $!\n"; my $cmd_table = { 'hibernate'=> sub{ $api->Call(1,0,0) }, 'suspend' => sub{ $api->Call(0,0,0) }, 'shutdown' => sub{ `shutdown -s -t 0` }, 'reboot' => sub{ `shutdown -r -t 0` }, 'logoff' => sub{ `shutdown -l -t 0` }, }; my $cmd = {'timeout' => $opts->{'timeout'}}; for my $key (keys %$opts) { next if $key =~ /help|timeout/; $cmd->{'name'} = $key; $cmd->{'run'} = $cmd_table->{$key}; last; } usage(qq/option requires "-h -s -r -d -l -? [-t]"/) unless $cmd->{'name'}; return $cmd; } sub count_down{ my $cmd = shift; my $command = uc $cmd->{'name'}; my $timeout = $cmd->{'timeout'}; local $SIG{'INT'} = 'abort'; print<<"EOM"; $command after $timeout seconds. Press [CTRL+C] to abort. EOM return if $timeout == 0; for my $sec (reverse (0..$timeout)) { printf "\r%3d sec", $sec; sleep 1; printf "\r%10s\r", ""; } } sub abort { print "\n\n", "abort!\n"; exit; } sub usage { my $msg = shift; print "$msg\n" if $msg; print<<"EOM"; - windows power control script v$VERSION - Usage : pwrctrl.pl option [timeout] pwrctrl.pl -d -t 60 # shutdown after 60sec Options : -? --help this document -h --hibernate hibernate -s --suspend suspend -r --reboot reboot -d --shutdown shutdown -l --logoff logoff [-t] [--timeout] run after n seconds default is 30 EOM exit; } __END__
rundll32.exe だと自動復帰できない
> rundll32.exe PowrProf.dll,SetSuspendState 0,1,0 > rundll32.exe PowrProf.dll,SetSuspendState Sleep
コマンドプロンプトからサスペンドするのに上記のコードがWEB上で見つかります。
サスペンドするだけなら問題ないみたいですが、 タスクスケジューラでの自動復帰ができません。
※Windows7 だと問題なく自動復帰できるようです。
Rundll32.exe は正しい使用法なのか?
まず、Rundll32.exe の仕様を確認します。Windows の Rundll と Rundll32 インターフェイス
これによると、Rundll32.exe は以下のように dll の関数呼び出しをする仕様です。
void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow); hwnd - DLL で作成する任意のウィンドウのオーナー ウィンドウとして 使用するウィンドウ ハンドル hinst - DLL のインスタンス ハンドル lpszCmdLine - DLL で解析する ASCIIZ コマンド ライン nCmdShow - DLL のウィンドウを表示する方法を指定
Rundll32.exe は dll の関数への引数を 文字列 として渡します。
引数は dll がパースしてねという仕様です。
次に、SetSuspendState の仕様を確認します。
SetSuspendState function - MSDNSetSuspendState の正しい引数は bool値 3つ。
C++ BOOLEAN WINAPI SetSuspendState( _In_ BOOLEAN Hibernate, _In_ BOOLEAN ForceCritical, _In_ BOOLEAN DisableWakeEvent );
1つ目は、 0 = サスペンド、 1 = ハイバネート
2つ目は、 0 = アプリケーションの終了を待って中断、 1 = 強制的に中断
3つ目は、 0 = wake イベント有効、 1 = wake イベント無効
これらを踏まえると、先の Rundll32.exe 経由の SetSuspendState 呼び出しはこうなります。
> rundll32.exe PowrProf.dll,SetSuspendState 0,1,0 SetSuspendState( hwnd, hinst, "0,1,0", nCmdShow );
引数を渡すと DisableWakeEvent の値が TRUE になるので
wake イベントで復帰できないというわけです。
つまり、Rundll32.exe 経由での SetSuspendState 呼び出しは間違ってます。
perl 以外の方法
1. SetSuspendState というソフトを使う。SetSuspendState - Vector
Sleep/Standby > SetSuspendState or > SetSuspendState 0 Hibernate > SetSuspendState 1
非常に簡易なソフト。
サスペンドか、ハイバネートするだけ。
秒数の指定とかもできないけど、そのへんはバッチなりなんなりで対応。
ソースを見ると下のように ForceCritical の値が TRUE なので注意が必要かも。
SetSuspendState( 0, 1, 0 ); // Sleep SetSuspendState( 1, 1, 0 ); // Hibernate
2. Ruby で SetSuspendState を実行。
コマンドラインからWindows PCを停止(スリープなど)する方法 | TipsZone
3. お好きな言語でAPIを叩く。
-------------------------
なんで shutdown.exe にサスペンド、ハイバネートのオプションないんでしょうね?
参考リンク
Windows の Rundll と Rundll32 インターフェイス
SetSuspendState function
SetSuspendState function (Windows)
Application.SetSuspendState メソッド (System.Windows.Forms)
プログラミングTips : SetSystemPowerState() の罠
コマンドラインからWindows PCを停止(スリープなど)する方法 | TipsZone
SetSuspendState - Vector
0 コメント:
コメントを投稿