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 コメント:
コメントを投稿