2012-09-23

perl - windows の電源コントロール

コマンドプロンプトのみでサスペンド(スリープ)は、できないみたいなので perl で挑戦。

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 - MSDN
C++
BOOLEAN WINAPI SetSuspendState(
    _In_  BOOLEAN Hibernate,
    _In_  BOOLEAN ForceCritical,
    _In_  BOOLEAN DisableWakeEvent
);
SetSuspendState の正しい引数は bool値 3つ。

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

2012-08-04

いまさら perl v5.5

もっぱら perl はローカルで使ってるので、バージョンやモジュールで困ることはないのですが、
久しぶりにプロバイダの提供スペースで CGI 使って遊ぼうと思い環境調査してみました。

#!/usr/local/bin/perl

use strict;
use warnings;

print "Content-type: text/html;\n\n";

print "perl : $]<br>\n";
print "$_ : $ENV{$_}<br>\n" for sort keys %ENV;

なぜか500エラー。
shebang は合ってるしなんで?
試しに use を外してみたら動きました。

古いといっても v5.8 だろうと思っていたところ
驚愕の v5.5.3 !!!!!

ちょっと手元の corelist さんに聞いてみましょう。
corelist は v5.8.9 以降なら標準で入ってるみたいです。

C:\>corelist Module::CoreList
Module::CoreList was first released with perl v5.8.9

C:\>corelist strict
strict was first released with perl 5

C:\>corelist warnings
warnings was first released with perl v5.6.0

C:\>corelist utf8
utf8 was first released with perl v5.6.0

C:\>corelist Encode
Encode was first released with perl v5.7.3

・・・。
strict しか使えないじゃないですか!!
いまさら EUC でコーディングとかしたくないなー。
正直、サーバーのエラーログをエラーまみれにしそうな予感しかしない。


perl v5.5.3 で使えないもの

・warnings.pm
・utf8.pm
・Encode.pm

# 3引数 open は perl v5.6 以降
open( INFO, ">", $datafile ) or die $!;

# 間接ファイルハンドルも perl v5.6 以降
open( my $in, "> $infile" )  or die $!;

# our も perl v5.6 以降
our $var;
# 代わりに use vars
use vars qw($var);
# perl v5.14 から一部の関数がリファレンスを受け取れるようになったので
# この書き方で楽してるとデリファレンスでエラーが出てハマルかも。

http://perldoc.perl.org/perl5140delta.html#Syntactical-Enhancements

|----------------------------+---------------------------|
| Traditional syntax         | Terse syntax              |
|----------------------------+---------------------------|
| push    @$arrayref, @stuff | push    $arrayref, @stuff |
| unshift @$arrayref, @stuff | unshift $arrayref, @stuff |
| pop     @$arrayref         | pop     $arrayref         |
| shift   @$arrayref         | shift   $arrayref         |
| splice  @$arrayref, 0, 2   | splice  $arrayref, 0, 2   |
| keys    %$hashref          | keys    $hashref          |
| keys    @$arrayref         | keys    $arrayref         |
| values  %$hashref          | values  $hashref          |
| values  @$arrayref         | values  $arrayref         |
| ($k,$v) = each %$hashref   | ($k,$v) = each $hashref   |
| ($k,$v) = each @$arrayref  | ($k,$v) = each $arrayref  |
|----------------------------+---------------------------|

まだまだハマリどころがありそう。
文字コード周りは Jcode.pm を使うのかな。


500 Internal Server Error の対応

500エラーが出たときにサーバーのエラーログが見れない場合どうするか。
CGI::Carp を使う。

C:\>corelist CGI::Carp
CGI::Carp was first released with perl 5.004
perl v5.4 からコアモジュールで安心。
# エラーをブラウザに表示
use CGI::Carp qw(fatalsToBrowser);

# エラーをログファイルに書き出す
BEGIN {
  use CGI::Carp qw(carpout);
  # perl v5.5.3 なので 3引数open は使えない
  open(LOG, ">> ./err.log") or die $!;
  carpout(LOG);
}

fatalsToBrowser はメジャーだけど
carpout は今回調べるまで知らなかった。

他にも KCatch.pm というモジュールもあります。


perl v5.5.3 環境を作る?

perl のバージョンを切り替える perlbrew というものがありますが、Windows だと使えないっぽい。

Strawberry Perl も ActivePerl も v5.5.3 のバイナリなんて置いてないしなー。
などとググってるうちに発見した Vector の Perl for Win32(perl5) は v5.5.7 らしい。
置いてあるディレクトリが win95 !!
これを入れる勇気はちょっとないなと。

いっそのこと、サーバー借りちゃった方が楽かもね。


参考リンク

History / Changes - perldoc.perl.org
コアドキュメント - perldoc.jp
Perl Hackers Hub:第14回 最新Perl使いこなし術―リファレンスの引き方,5.10以降の新機能(2)
Perl Hackers Hub:第14回 最新Perl使いこなし術―リファレンスの引き方,5.10以降の新機能(3)
perl5.10から5.16の新機能まとめ

CGI::Carp - search.cpan.org
[Perl] KCatch.pm/CGI エラーメッセージをブラウザに表示
Jcode - search.cpan.org

perlbrew - search.cpan.org

Windows で複数の perl を使い分けよう! - JPerl Advent Calendar 2010 Win32 Track
Strawberry Perl for Windows
ActivePerl

2012-06-14

Firefoxの検索クエリ変更

Firefox13 がリリースされたので入れてみました。
Firefox7 以降入れてなかったんですが、起動も速いし動作も速い。
いつの間にやらディスクキャッシュからメモリキャッシュになったんですね。

Sleipnir のキャッシュをRAMディスクにして使用してますが
それよりも若干早い感じがします。

Firefox はカスタマイズするのにアドオン探さなきゃいけないのが面倒なので
いまいち移行しきれないのですが、1つ障害が解決したのでメモ。

長年、検索窓のクエリ変更がうまくいかなかった謎が解決。
searchplugin を変更後に Profile 内の search.json を削除。
これで変更が反映されます。

次は google 検索を変更。
インスタント検索が始まってから、検索クエリに num=50 を足しても
表示件数が変更できなかったのですが、以下の手順で変更できます。


Firefoxが起動してない状態で
・Mozilla Firefox/searchplugins/google-jp.xml を変更
<Url type="text/html" method="GET" template="http://www.google.co.jp/search">
  <Param name="q" value="{searchTerms}"/>
  <Param name="complete" value="0"/> <!--インスタント検索オフ-->
  <Param name="as_qdr" value="all"/> <!--期間-->
  <Param name="num" value="50"/> <!--表示件数-->
  <Param name="gbv" value="1"/>
</Url>
・Profile内の search.json を削除


Sleipnir だと
http://www.google.co.jp/search?q={all}&num=50
だけで50件表示できるんですけど何でですかね?

2012-05-04

perl - Markdown変換スクリプト

ネタを整理するのに Markdown で書くと便利なのかなと調べていると
CPAN に Text::Markdown というモジュールがあったので
思いつきで変換スクリプト書いてみました。

スクリプトにファイル名を渡すか、__END__ 以下に Markdown を書いて使います。
出力を保存する場合はリダイレクトして下さい。

# markdown2html.pl

use strict;
use warnings;

use Text::Markdown 'markdown';

my $file = shift;
my $text = "";

if ($file) {
 open(my $fh, '<', $file) or die($!);
  local $/;
  $text = <$fh>;
 close $fh;
} else {
 local $/;
 $text = <DATA>;
}
my $html = markdown($text);

#print $text;
print $html;


__END__
#markdown sample
##h2
###h3

paragraph  
hoge
moge

[リンク文字列](http://example.com/)
[リンク文字列](http://example.com/ "タイトル")
<http://example.com>

![画像の説明](/path/to/image.jpg "タイトル")

- listA
- listB
- listC

---

1. listA
2. listB
3. listC

>blockquote
blockquote

    ...code...
`...code...`
ドラッグアンドドロップ用にバッチファイルも付けておきますね。
スクリプトと保存先のパスは適宜修正して下さい。
::markdown2html.bat

set markdown2html="C:\markdown2html.pl"
set output="C:\%~n1%.htm"

perl %markdown2html% %1 > %output%

pause

Markdown用のエディタとかもあるので、わざわざこんなの使うことないですよね。
個人的には秀丸から perl に渡して処理とかよくやるので、使うかなと思いまして。

参考リンク

Tomas Doran / Text-Markdown - search.cpan.org
Daring Fireball: Markdown
blog::2310 » Markdown文法の全訳
Markdown - Wikipedia

2012-05-02

rtmpdump - v2.5 Unofficial binaries

RTMPDump v2.5 GIT-2012-03-31 (Handshake 10 support by Xeebo)
http://stream-recorder.com/forum/showpost.php?p=44648&postcount=96

--swfVfy | -W オプションは Type2 未対応らしくこんなエラーが出ます。
ERROR: HandleCtrl: SWFVerification Type 2 request not supported,
       attempting to use SWFVerification Type 1! Patches welcome...