IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

perl の Win32::GuiTest モジュールを使ってみる。

はじめに

久々の perl。突っ込まれるのを期待しつつやってみます。

準備

PATH を通して、コマンドプロンプトを起動して、

> cpan -i Win32::GuiTest

でおk。

何から始めよう

IE8 をどこまで操作できるかやってみる。

IE8 を起動

use strict;
use warnings;
use utf8;

system('explorer http://www.google.co.jp/');


おおお。

IE8 のウィンドウハンドルを取得

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

# IE8 が立ち上がるまで待つ
# FindWindowLike の第一引数が 0 だと、デスクトップ上の全ウィンドウに対して検索してくれる
sleep(0.2) while (!scalar(FindWindowLike(0, 'Windows Internet Explorer$')));

# IE8 のウィンドウハンドルを表示
print Dumper([FindWindowLike(0, 'Windows Internet Explorer$')]);


おおお
(続く)

WindowText と ClassName を取得してみる。

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

# IE8 が立ち上がるまで待つ
# FindWindowLike の第一引数が 0 だと、デスクトップ上の全ウィンドウに対して検索してくれる
sleep(0.2) while (!scalar(FindWindowLike(0, 'Windows Internet Explorer$')));

# IE8 のウィンドウハンドルを表示
print Dumper([map {
        [ GetWindowText($_), GetClassName($_) ]
    } FindWindowLike(0, 'Windows Internet Explorer$')]);


あれれ、タイトルと WindowText が違うなー。

原因は?
  • Googe が読み込まれる前だったから。

そっかー。そういえば HTML の内容で WindowText は変化するから、 ClassName からウィンドウハンドルを取得したほうがいいかもなー。

ちょっと修正

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

my $win = do {
    my @wins;

    # ClassName からウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@wins = FindWindowLike(0, undef, '^IEFrame$')));

    # 1 画面だけ開いている前提で
    die 'There should be one IE8 Window.' if (scalar(@wins) != 1);

    shift @wins;
};

print Dumper({
    title => GetWindowText($win),
    class => GetClassName($win)
});


ちょっと修正した

IE8 のメインウィンドウが持っている子ウィンドウの情報を出す

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

my $win = do {
    my @wins;

    # ClassName からウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@wins = FindWindowLike(0, undef, '^IEFrame$')));

    # 1 画面だけ開いている前提で
    die 'There should be one IE8 Window.' if (scalar(@wins) != 1);

    shift @wins;
};

print Dumper([
    map {
        # これで $win からの深さが取れるみたい
        '--' x GetChildDepth($win, $_) . ' ' .
        GetWindowText($_) . ' (' . GetClassName($_) . ')'
    } GetChildWindows($win)
]);


UTF8 なので表示できてない場所があるよ!助けてダンコーガイ

Encode 使う

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);
use Encode;

# cp932 (Windows で使われてる Shift-jis 的な何か
my $cp932 = find_encoding('cp932');

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

my $win = do {
    my @wins;

    # ClassName からウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@wins = FindWindowLike(0, undef, '^IEFrame$')));

    # 1 画面だけ開いている前提で
    die 'There should be one IE8 Window.' if (scalar(@wins) != 1);

    shift @wins;
};

# 子孫ウィンドウ
for (GetChildWindows($win)) {

    # 出力は cp932 で
    print $cp932->encode(
        # $win からの深さ
        '--' x GetChildDepth($win, $_) . ' ' .
        GetWindowText($_) . ' (' .
        GetClassName($_) . ")\n"
    );
}


日本語出た

ロケーションバーに WM_GETTEXT を送って URL を取る

Win32::GuiTest では WMGetText という関数を使えば WM_GETTEXT を遅れるみたい。

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);
use Encode;

# cp932 (Windows で使われてる Shift-jis 的な何か
my $cp932 = find_encoding('cp932');

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

my ($win, $address_band_root, $address_edit, $url) = do {
    my (@wins, $win, @address_band_roots, $address_band_root, @address_edits, $address_edit, $url);

    # IEFrame のウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@wins = FindWindowLike(0, undef, '^IEFrame$')));
    die 'There should be one IE8 Window.' if (scalar(@wins) != 1);
    $win = shift @wins;

    # Address Band Root のウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@address_band_roots = FindWindowLike($win, undef, 'Address Band Root')));
    die 'There should be one IE8 Address Band Root.' if (scalar(@address_band_roots) != 1);
    $address_band_root = shift @address_band_roots;

    # Address Band Root の子孫の Edit を探す
    sleep(0.2) while (!scalar(@address_edits = FindWindowLike($address_band_root, undef, 'Edit')));
    die 'There should be one IE8 Address Edit.' if (scalar(@address_edits) != 1);
    $address_edit = shift @address_edits;

    # Edit の内容が入るまで待つ
    sleep(0.2) while(($url = WMGetText($address_edit)) eq '');

    # IEFrame, Address Band Root, Edit ウィンドウクラスと WMGetText の結果を返す
    ($win, $address_band_root, $address_edit, $url);
};

print $cp932->encode($url . "\n");


おおお。取れた取れた(嬉しい)

ロケーションバーにキーボードイベントを送ってページ遷移

use strict;
use warnings;
use utf8;

use Data::Dumper;
use Win32::GuiTest qw(:ALL);
use Encode;

# cp932 (Windows で使われてる Shift-jis 的な何か
my $cp932 = find_encoding('cp932');

# これをやっておかないと中で shift-jis 前提で扱われるみたい
UnicodeSemantics(1);

system('explorer http://www.google.co.jp/');

my ($win, $address_band_root, $address_edit, $url) = do {
    my (@wins, $win, @address_band_roots, $address_band_root, @address_edits, $address_edit, $url);

    # IEFrame のウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@wins = FindWindowLike(0, undef, '^IEFrame$')));
    die 'There should be one IE8 Window.' if (scalar(@wins) != 1);
    $win = shift @wins;

    # Address Band Root のウィンドウハンドルを取得
    sleep(0.2) while (!scalar(@address_band_roots = FindWindowLike($win, undef, 'Address Band Root')));
    die 'There should be one IE8 Address Band Root.' if (scalar(@address_band_roots) != 1);
    $address_band_root = shift @address_band_roots;

    # Address Band Root の子孫の Edit を探す
    sleep(0.2) while (!scalar(@address_edits = FindWindowLike($address_band_root, undef, 'Edit')));
    die 'There should be one IE8 Address Edit.' if (scalar(@address_edits) != 1);
    $address_edit = shift @address_edits;

    # Edit の内容が入るまで待つ
    sleep(0.2) while(($url = WMGetText($address_edit)) eq '');

    # IEFrame, Address Band Root, Edit ウィンドウクラスと WMGetText の結果を返す
    ($win, $address_band_root, $address_edit, $url);
};

# ロケーションバーにフォーカスさせて
SetFocus($address_edit);

# key を送る("~" はエンターキーの意味)
SendKeys('http://www.yahoo.co.jp/~');


あー。ダメだ。Google のトップページの window.onload で、検索ボックスにフォーカス取られちゃうなー。
結果として、検索されちゃった

ロケーションバーの値を直接書き換えて、移動ボタンを押すようにしたい。

移動ボタンを押すには、 Toolbar に SendMessage して構造体を取得しなきゃダメみたい。
SendMessage 自体は Win32::GuiTest で出来るのだけど「構造体の領域を確保して、そのポインタを渡す」なんてことはできるのかなー。
なんか、 AllocateVirtualBuffer を使えば共有メモリをアロケートできそうだ。でも、 TBBUTTON の sizeof を perl から取れない><
やっぱ、 XS しなきゃだめなのかな?

中途半端だけど

もう遅いので寝ます。
来週末あたりにもう一度調べよう。