どうもあまちゃんです。 突然ですが、 Win32::GuiTest というモジュールを使うと Windows の GUI アプリを楽しくハックする事ができます。
使う側は特にめんどうくさいことをしなくても (時には別プロセスに入り込んで)様々な情報を取得してきたり設定してきたりしてくれます。
Strawberry Perl を使っているなら普通に
C:\> cpan -i Win32::GuiTest
でインストールできます。 ActivePerl を使っている場合は、PPM があります。
ソースはコピペすれば動くと思いますよっと。UTF-8 で書いてます。
use strict;
use warnings;
use utf8;
# ↑ Perl ハッカーに DIS られなくなるおまじない
# Win32::GuiTest を使うおまじない
use Win32::GuiTest qw(:ALL);
# 日本語を使えるようにするおまじない
UnicodeSemantics(1);
# ここで Win32::GuiTest を使う
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
# 小数点もいけちゃう sleep 関数
use Time::HiRes qw(sleep);
UnicodeSemantics(1);
for (my $i = 0; $i < 500; $i++) {
# 10 ms 待つ
sleep(0.01);
# マウスを動かす
MouseMoveAbsPix(cos($i / 10) * 400 + 400, sin($i / 10) * 400 + 400);
}
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
UnicodeSemantics(1);
# デスクトップウィンドウの取得
my $desktop_win = GetDesktopWindow();
# デスクトップウィンドウの矩形の取得
my ($left, $top, $right, $bottom) = GetWindowRect($desktop_win);
# (left, top は 0 だよねーっと)一応確認
die "Oops!" if $left != 0 || $top != 0;
for (my $i = 0; $i < 500; $i++) {
sleep(0.01);
# デスクトップ全体をマウスが回る(この爽快感!)
MouseMoveAbsPix(
cos($i / 10) * $right / 2 + $right / 2,
sin($i / 10) * $bottom / 2 + $bottom / 2
);
}
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
UnicodeSemantics(1);
# デスクトップウィンドウの取得(全部の親)
my $desktop_win = GetDesktopWindow();
# (デスクトップウィンドウにはお父さんウィンドウいないよね><?)一応確認
die "Oops!" if GetParent($desktop_win) != 0;
# デスクトップウィンドウの全子孫を走査
for my $child (GetChildWindows($desktop_win)) {
# ここで各ウィンドウ($child)にあんなことやこんなことをする
}
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
# cmd.exe のエンコーディングが CP932 なので
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
my $desktop_win = GetDesktopWindow();
die "Oops!" if GetParent($desktop_win) != 0;
for my $child (GetChildWindows($desktop_win)) {
# ウィンドウの深さ
my $window_depth = GetChildDepth($desktop_win, $child);
# ウィンドウのクラス名(種類)
my $class_name = GetClassName($child);
# ウィンドウの名前
my $window_text = GetWindowText($child);
# 表示
print $cp932->encode('--' x $window_depth . $window_text . '(' . $class_name . ")\n");
}
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
# Windows のスタートメニューのスタートボタンのウィンドウ
my ($win, @wins) = FindWindowLike(0, '^スタート$');
# 一個だけだよね><?一応確認
die 'Oops!' if @wins;
# 情報の表示
print $cp932->encode(GetWindowText($win) . '(' . GetClassName($win) . ")\n");
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
UnicodeSemantics(1);
my ($win, @wins) = FindWindowLike(0, '^スタート$');
die 'Oops!' if @wins;
# ボタンの座標を取得
my ($x, $y) = GetWindowRect($win);
# マウスをボタン上に移動
MouseMoveAbsPix($x + 1, $y + 1);
# クリック!
SendLButtonDown();
SendLButtonUp();
# FindWindowLike から SendLButton*() までを一発でやってくれる
# MouseClick という関数もありますが、今回は使いません
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
# 無名関数の再帰呼び出し用
use Devel::Caller qw(caller_cv);
UnicodeSemantics(1);
# メモ帳の起動
system('start notepad');
# メモ帳の起動を待つ
sleep(0.5);
# メモ帳のウィンドウを取得
my ($notepad) = FindWindowLike(0, 'メモ帳$');
# メモ帳を最前に持ってくる
SetForegroundWindow($notepad);
# メニューツリーを再帰的に走査
(sub {
my ($menu, $depth) = @_;
# メニューアイテムの数を取得
my $count = GetMenuItemCount($menu);
# メニューアイテムを走査
for (my $i = 0; $i < $count; $i ++) {
# メニューアイテムの情報を取得
my $info = { GetMenuItemInfo($menu, $i) };
# 表示
print '--' x $depth . $info->{text} . "\n" if $info->{type} eq 'string';
# サブメニューを表示(再帰)
caller_cv(0)->(GetSubMenu($menu, $i), $depth + 1);
}
})->(GetMenu($notepad), 0); # メモ帳のメインメニューを渡す
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
UnicodeSemantics(1);
system('start notepad');
sleep(0.5);
my ($notepad) = FindWindowLike(0, 'メモ帳$');
SetForegroundWindow($notepad);
# 0 番目のメニューアイテムからサブメニューを取得して、
# サブメニューから 1 番目のメニューアイテムの ID を取得してくる
# (メモ帳では、「ファイル」→「開く」メニュー
my $id = GetMenuItemID(GetSubMenu(GetMenu($notepad), 0), 1);
# WM_COMMAND メッセージでメモ帳に、メニューが選択されたと教えてあげる
PostMessage($notepad, Win32::GuiTest::WM_COMMAND, $id, 0);
# MenuSelect っていうのもあるのですが、
# 日本語を CP932 で指定しなければならず、
# しかも、フルの名前を指定しないといけないので、めんどうです。
# 今回は使いません><
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
system('start notepad');
sleep(0.5);
my ($notepad) = FindWindowLike(0, 'メモ帳$');
SetForegroundWindow($notepad);
# エディットボックスを取得
my ($edit) = FindWindowLike($notepad, undef, '^Edit$');
# WMSetText を使って、エディットボックスの値を直接設定
WMSetText($edit, $cp932->encode('ほげほげ'));
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
UnicodeSemantics(1);
system('start notepad');
sleep(0.5);
my ($notepad) = FindWindowLike(0, 'メモ帳$');
SetForegroundWindow($notepad);
# エディットボックスを取得
my ($edit) = FindWindowLike($notepad, undef, '^Edit$');
# フォーカスを合わせる
SetFocus($edit);
# キーボード入力をエミュレート
SendKeys('hoge{ENTER}hoge{ENTER}fuga{ENTER}piyo');
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
system('start notepad');
sleep(0.5);
my ($notepad) = FindWindowLike(0, 'メモ帳$');
SetForegroundWindow($notepad);
my ($edit) = FindWindowLike($notepad, undef, '^Edit$');
WMSetText($edit, $cp932->encode('ほげほげ'));
# エディットボックスのデータを取得
# (CP932 で帰ってくるので、そのまま print してるけど、プログラム中で扱う時は decode すべき)
print WMGetText($edit) . "\n";
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
# レジストリエディタを起動
system('start regedit');
# 待つ
sleep(0.5);
# ウィンドウを取得
my ($regedit) = FindWindowLike(0, undef, '^RegEdit_RegEdit$');
# 最前面に持ってくる
SetForegroundWindow($regedit);
# 左のツリービューを取得
my ($tree) = FindWindowLike($regedit, undef, '^SysTreeView32$');
# ツリービューを選択する
# この場合は、 Firefox のレジストリキーを読みに行きます
SelTreeViewItemPath($tree, $cp932->encode('マイ コンピュータ|HKEY_LOCAL_MACHINE|SOFTWARE|Mozilla|Firefox'));
# この関数は、エクスプローラーでも威力を発揮します。
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
# レジストリエディタを起動
system('start regedit');
# 待つ
sleep(0.5);
# ウィンドウを取得
my ($regedit) = FindWindowLike(0, undef, '^RegEdit_RegEdit$');
# 最前面に持ってくる
SetForegroundWindow($regedit);
# 左のツリービューを取得
my ($tree) = FindWindowLike($regedit, undef, '^SysTreeView32$');
# ツリービューを選択する
SelTreeViewItemPath($tree, $cp932->encode('マイ コンピュータ|HKEY_CLASSES_ROOT|CompressFolder'));
# 右のリストビューを取得
my ($list) = FindWindowLike($regedit, undef, '^SysListView32$');
my ($x, $y, $posbuf);
# $list を持っているプロセス(regedit のプロセス)
# のメモリ空間を 8 バイト確保
$posbuf = AllocateVirtualBuffer($list, 8);
# メモリ確保出来たら
if ($posbuf) {
# エラーのときもちゃんとレスキューされるように
eval {
# $list に 0x1010 (LVM_GETITEMPOSITION) というメッッセージを送る
# 結果を返して欲しい共有メモリのポインタを渡す
# 第三引数の 1 は「(0 から数えて)1 番目のリストアイテム」という意味
SendMessage($list, 0x1010, 1, $posbuf->{ptr});
# 共有メモリから、結果を読み出す
# $x, $y にはリストアイテムの位置を取得
($x, $y) = unpack('L2', ReadFromVirtualBuffer($posbuf, 8));
};
# 共有メモリを解放
FreeVirtualBuffer($posbuf);
# もし、 eval 中にエラーがあれば
die $@ if $@;
}
# リストビューの位置を取る
my ($px, $py) = GetWindowRect($list);
# マウスを移動
MouseMoveAbsPix($px + $x + 4, $py + $y + 4);
# ダブルクリック!
SendLButtonDown();
SendLButtonUp();
SendLButtonDown();
SendLButtonUp();
# 実装のほうで SetWindowsHookEx を使ってる部分が動かないことがある(原因不明)ので
# この例のように、
# 自分で共有メモリ(AllocateVirtualBuffer)を使って生のメッセージでやるというパターンは結構あります。
use strict;
use warnings;
use utf8;
use Win32::GuiTest qw(:ALL);
use Time::HiRes qw(sleep);
use Encode;
my $cp932 = find_encoding('cp932');
UnicodeSemantics(1);
# エクスプローラーの起動
system('start explorer');
# 待つ
sleep(0.5);
# ウィンドウを取得
my ($exp) = FindWindowLike(0, undef, '^ExploreWClass$');
# 最前面に持ってくる
SetForegroundWindow($exp);
# 一番最初のツールバーを取得
# (ツールバーはたいていいくつかある)
my ($toolbar) = FindWindowLike($exp, undef, '^ToolbarWindow32$');
my ($x, $y, $rectbuf);
# $toolbar を持っているプロセス(エクスプローラーのプロセス)
# のメモリ空間を 16 バイト確保
$rectbuf = AllocateVirtualBuffer($toolbar, 16);
# メモリ確保出来たら
if ($rectbuf) {
# エラーのときもちゃんとレスキューされるように
eval {
# $toolbar に 0x4ld (TB_GETITEMRECT) というメッッセージを送る
# 結果を返して欲しい共有メモリのポインタを渡す
# 第三引数の 7 は
# 「(0 から数えて)7 番目のツールバーアイテム(セパレータや hide されているアイテムも含む)」という意味
SendMessage($toolbar, 0x41d, 7, $rectbuf->{ptr});
# 共有メモリから、結果を読み出す
# $x, $y にはリストアイテムの位置を取得
($x, $y) = unpack('L4', ReadFromVirtualBuffer($rectbuf, 16));
};
# 共有メモリを解放
FreeVirtualBuffer($rectbuf);
# もし、 eval 中にエラーがあれば
die $@ if $@;
}
# ツールバーの位置を取る
my ($px, $py) = GetWindowRect($toolbar);
# マウスを移動
MouseMoveAbsPix($px + $x + 4, $py + $y + 4);
# クリック!
SendLButtonDown();
SendLButtonUp();
このように、めんどくさい Windows の GUI での作業を Perl を使ってある程度自動化しておくことが出来ます。
また、これらを CGI 化して GreaseMonkey とかで呼び出すと非常に便利です><でも、大変危険ですので絶対に真似しないようにしてください><
というわけで、次は id:TAKESAKO さんお願いします。