今回やるのは、前回と同じく EnumWindows 関数を使用したウィンドウの列挙なのであります。
前回は、アドレスや最大数などの情報を直接マシン語のバイナリコードの中に書き込んでいくということを行なっていましたね。これは、今のように小さな関数ならばそれほど問題ではないのですが、関数が大きくなるにつれて、これらの情報をどこに格納するのか(オフセット)などの情報が複雑になってくるでしょうし、ちょっとしたミスでプログラムが大暴走、ということも考えられます。
今回は、より安全にプログラミングを行なうということを考えてみましょう。で、どうすればいいのかというと、マシン語の中に直接埋め込むデータが少なければ少ないほどいいわけです。そこで今回は、HSPスクリプトから関数側に渡されるすべての情報をあらかじめ構造体(HSP側から見れば配列変数)に格納しておき、この構造体のアドレス1つだけを関数に渡す(埋め込む)ということにします。
しかも今回の場合は、 EnumWindows 関数の lParam パラメータに指定された値がそのまま EnumWindowsProc の lParam パラメータに渡されるので、これを構造体アドレスとすれば、マシン語コード内には何も埋め込まずに済みますよね。
ということで、今回は以下のよう ENUMWND_DATA 構造体を定義してみることにしましょう。
typedef struct { HWND *phwnd; // ハンドルを格納する配列変数アドレス int nmax; // 最大数(配列の要素数) int ncntr; // カウンタ } ENUMWND_DATA;
この構造体の実体は、HSP側で準備された配列変数になります。注意するべきことは、前回はカウンタ変数のアドレスを関数に埋め込んでいましたが、この構造体はメンバそのものがカウンタになっているという点です。
コールバック関数のソースコードは以下のようになります。
#include <windows.h> // HSPからの情報を渡すための構造体 typedef struct { HWND *phwnd; // ハンドルを格納する配列変数アドレス int maxnum; // 最大数(配列の要素数) int cnt; // カウンタ } ENUMWND_DATA; BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { // lParam は ENUMWND_DATA 構造体アドレス ENUMWND_DATA *pData = (ENUMWND_DATA *)lParam; if (pData->cnt >= pData->maxnum) return FALSE; pData->phwnd[pData->cnt] = hwnd; pData->cnt++; return TRUE; }
このソースコードをコンパイルして作成されたリスティングファイル( .cod )は以下のようになります。
(…………………… 省 略 ……………………) PUBLIC _EnumWindowsProc@8 ; COMDAT _EnumWindowsProc@8 _TEXT SEGMENT _hwnd$ = 8 _lParam$ = 12 _EnumWindowsProc@8 PROC NEAR ; COMDAT ; File C:\My Documents\C_obj\Hspmcn\enumwnd2.c ; Line 15 00000 8b 44 24 08 mov eax, DWORD PTR _lParam$[esp-4] 00004 8b 48 08 mov ecx, DWORD PTR [eax+8] 00007 8b 50 04 mov edx, DWORD PTR [eax+4] 0000a 3b ca cmp ecx, edx 0000c 7c 05 jl SHORT $L73115 ; Line 16 0000e 33 c0 xor eax, eax ; Line 20 00010 c2 08 00 ret 8 $L73115: ; Line 17 00013 8b 10 mov edx, DWORD PTR [eax] 00015 56 push esi 00016 8b 74 24 08 mov esi, DWORD PTR _hwnd$[esp] 0001a 89 34 8a mov DWORD PTR [edx+ecx*4], esi ; Line 18 0001d 8b 48 08 mov ecx, DWORD PTR [eax+8] 00020 41 inc ecx 00021 5e pop esi 00022 89 48 08 mov DWORD PTR [eax+8], ecx ; Line 19 00025 b8 01 00 00 00 mov eax, 1 ; Line 20 0002a c2 08 00 ret 8 _EnumWindowsProc@8 ENDP _TEXT ENDS END
生成されたマシン語を変数に格納するのですが、前回のようにして直接手で打つのは、ミスによるバグを招く確率が非常に高いですね。今回はちょっと別の方法です。とりあえず、下のスクリプトを実行してみてください。
; マシン語コード変換スクリプト #include "llmod.as" varname = "fncode" ; 変数名 numline = 6 ; 1行の要素数 sdim cod, 10000 ; 出力されたマシン語コード cod = {" 8b 44 24 08 8b 48 08 8b 50 04 3b ca 7c 05 33 c0 c2 08 00 8b 10 56 8b 74 24 08 89 34 8a 8b 48 08 41 5e 89 48 08 b8 01 00 00 00 c2 08 00 "} ; 文字列形式からバイナリコードに変換 dim bin, 800 ll_bin bin, cod if stat : dialog "マシン語に変換できませんでした。", 1 : end ; dllret には変換サイズが格納されます n = dllret + 3 / 4 ; バイナリコードの配列変数の要素数 dim buf, 10000 buf = "xdim "+varname+", "+n+"\n" repeat if cnt == n : break if cnt \ numline == 0 : buf += varname+"."+cnt+" = " t = bin.cnt : str t, 24 : buf += "$"+t if (cnt+1\numline!=0)&(cnt+1!=n) : buf += ", " : else : buf += "\n" loop mesbox buf, winx, winy, 4 stop
上のスクリプトは、 loadlib.dll の ll_bin 命令を使用して、文字列形式で記述されているマシン語コードをバイナリ形式に変換し、それを HSP の変数に格納するためのスクリプトを作成します。このスクリプトを実行させると、以下のコードが取得できます。これをコピーしてそのままスクリプトに貼り付けてしまいましょう。
xdim fncode, 12 fncode.0 = $0824448b, $8b08488b, $ca3b0450, $c033057c, $8b0008c2, $748b5610 fncode.6 = $34890824, $08488b8a, $48895e41, $0001b808, $08c20000, $00000000
HSP スクリプトは以下のようになります。今回はマシン語コード中に埋め込む情報はありません。代わりに、上で定義した ENUMWND_DATA 構造体となる配列変数に必要な情報を格納し、その変数のアドレスを EnumWindows 関数の lParam パラメータに渡さなくてはなりません。また、呼び出し前にはカウンタとなるメンバ(ここでは変数 ewdata.2)を 0 に初期化するのも忘れてはいけません。
#include "llmod.as" #include "xdim.as" ; コールバック関数のマシン語コードを変数に格納 xdim fncode, 12 fncode.0 = $0824448b, $8b08488b, $ca3b0450, $c033057c, $8b0008c2, $748b5610 fncode.6 = $34890824, $08488b8a, $48895e41, $0001b808, $08c20000, $00000000 ; ウィンドウハンドルを格納する配列変数 dim hwnd, 512 ; ENUMWND_DATA 構造体 getptr ewdata.0, hwnd ; 配列変数のアドレス ewdata.1 = 512 ; 最大数(配列の要素数) ewdata.2 = 0 ; カウンタ(0 に初期化する) ; EnumWindows の呼び出し getptr pm.0, fncode ; コールバック関数(マシン語コード)アドレス getptr pm.1, ewdata ; ENUMWND_DATA 構造体アドレス dllproc "EnumWindows", pm, 2, D_USER dialog "" + ewdata.2 + "個のウィンドウハンドルが取得されました。" stop
前回よりもすっきりした感じがします。安全性も、前回よりずっと高まってます。