さて、実際のスクリプト書いて見ましょう。
今回はメインプロセスからメモ帳を実行し、メモ帳が閉じられたらメインプロセスが実行を再開するようにしてみましょう。
#include "llmod.as"
; 起動する実行可能ファイル名(コマンドラインパラメータも含む)
cmd = "notepad.exe"
; STARTUPINFO構造体を設定
dim stinfo, 17
stinfo.0 = 68 ; 構造体サイズ(cbメンバ)
; PROCESS_INFOMATION構造体を設定
dim prinfo, 4
; 新しいプロセスを起動
pm.0 = 0 ; 実行可能ファイル名はパラメータとともに指定するのでNULL
getptr pm.1, cmd ; 実行可能ファイル名文字列のアドレス
pm.2 = 0
pm.3 = 0
pm.4 = 0
pm.5 = 0
pm.6 = 0
pm.7 = 0 ; 起動時カレントディレクトリは親プロセスと同じ
getptr pm.8, stinfo ; STARTUPINFO構造体アドレス
getptr pm.9, prinfo ; PROCESS_INFOMATION構造体アドレス
dllproc "CreateProcessA", pm, 10, D_KERNEL
if dllret == 0 : dialog "起動失敗" : end
hprocess = prinfo.0 ; プロセスのハンドル
hthread = prinfo.1 ; プライマリスレッドのハンドル
; プライマリスレッドのハンドルは直ちにクローズ
dllproc "CloseHandle", hthread, 1, D_KERNEL
hthread = 0
; サブプロセスの終了を待機する
pm.0 = hprocess ; プロセスのハンドル
pm.1 = -1 ; INFINITE
dllproc "WaitForSingleObject", pm, 2, D_KERNEL
dialog "サブプロセスが終了しました。"
; プロセスハンドルをクローズ
dllproc "CloseHandle", hprocess, 1, D_KERNEL
hprocess = 0
end
前回も述べましたが、普通に起動させるのであれば、STARTUPINFO構造体には特に何も指定する必要はありません。cbメンバに構造体サイズ68を格納し、それ以外のメンバはすべて0になるように指定します。
今回はCreateProcess関数の第2引数に実行ファイル名「notepad.exe」を指定しています。なぜ第1引数に指定しないのかというと、第1引数に実行ファイル名を指定する場合は、フルパス指定か、さもなくばカレントディレクトリの実行ファイルを指定することしかできないためです。第2引数に(必要ならコマンドラインパラメータも添えて)指定すると、実行ファイルをカレントディレクトリのほかにWindowsディレクトリやシステムディレクトリからも検索してくれます。また、拡張子「.exe」を省略することもできます。メモ帳はWindowsディレクトリ内にあるので、こうしなければならないのです。
CreateProcess関数を呼び出したら、必要のないプライマリスレッドのハンドルはすぐにCloseHandle関数を使ってクローズしておきます。その後、プロセスのハンドルを引数としてWaitForSingleObject関数を呼び出します。メモ帳が終了するまではここで処理が中断されます。処理が再開されたら、プロセスのハンドルをクローズします。
上のサンプルを実行させてみるとわかりまっすが、サブプロセスが終了しているのを待機している間は、メインプロセスのウィンドウはまったく反応しません。WaitForSingleObject関数の待ち時間として-1 (INFINITE) を指定しているため、サブプロセスが終了するまで無限に待ち続けます。その間、ウィンドウの移動やサイズ変更、プロセスの終了もできないというのは、少々困るような気もします。
これらの問題を解決する手段がないというわけではありません。例えば、以下の方法で行なうこともできるでしょう。
WaitForSingleObject関数の待ち時間として、-1以外の適当な値(あるいは0)を指定するというものです。このとき、ループを作成して、関数が戻り値0 (WAIT_OBJECT_0) を返すまで繰り返し呼び出します。
この方法では、サブプロセスが終了したかどうかをループ中で監視をするのですが、ウィンドウの処理に対応するためにはこのループ中にwaitまたはawaitを実行する必要があります。
この方法ならば、プロセス終了を監視しながら他の処理を平行して行なうこともできます。
MsgWaitForMultipleObjects関数は、サブプロセスの終了時だけでなく、スレッドが所有するウィンドウに対してウィンドウメッセージが送られた(ポストされた)ときにも実行を再開します。メッセージについてはここでは詳しく述べませんが、これはウィンドウに対して何らかのイベント(マウスの移動・クリックやキー入力、移動、サイズ変更、終了など)が発生したときに送られてくるものです。
この方法では、上の方法のように他の処理を平行して行なうということはできませんが、ウィンドウ操作やマウスクリックに対して反応するなどといったことは可能になります。
ただしこの場合は、メッセージが送られたことで実行が再開されたときにはwaitまたはawaitを実行する必要があります。
ここでは、上の2つの方法のうち、タイムアウト時間を指定する方法のみを紹介しておきましょう。
#include "llmod.as"
; 起動する実行可能ファイル名(コマンドラインパラメータも含む)
cmd = "notepad.exe"
; STARTUPINFO構造体を設定
dim stinfo, 17
stinfo.0 = 68 ; 構造体サイズ(cbメンバ)
; PROCESS_INFOMATION構造体を設定
dim prinfo, 4
; 新しいプロセスを起動
pm.0 = 0 ; 実行可能ファイル名はパラメータとともに指定するのでNULL
getptr pm.1, cmd ; 実行可能ファイル名文字列のアドレス
pm.2 = 0
pm.3 = 0
pm.4 = 0
pm.5 = 0
pm.6 = 0
pm.7 = 0 ; 起動時カレントディレクトリは親プロセスと同じ
getptr pm.8, stinfo ; STARTUPINFO構造体アドレス
getptr pm.9, prinfo ; PROCESS_INFOMATION構造体アドレス
dllproc "CreateProcessA", pm, 10, D_KERNEL
if dllret == 0 : dialog "起動失敗" : end
hprocess = prinfo.0 ; プロセスのハンドル
hthread = prinfo.1 ; プライマリスレッドのハンドル
; プライマリスレッドのハンドルは直ちにクローズ
dllproc "CloseHandle", hthread, 1, D_KERNEL
hthread = 0
; 終了時にジャンプする
onexit *lb_onexit
*lb_mainloop
; HSPウィンドウへの処理に対応するためには wait または await が必要
await 0
; サブプロセスの終了を待機する
pm.0 = hprocess ; プロセスのハンドル
pm.1 = 100 ; タイムアウト時間(100ミリ秒)
dllproc "WaitForSingleObject", pm, 2, D_KERNEL
if dllret == 0 { ; 戻り値 WAIT_OBJECT_0
; シグナル状態になった(サブプロセスが終了した)場合
dialog "サブプロセスが終了しました"
goto *lb_quit
} else {
; タイムアウト時間が経過した場合
; (このときの戻り値は $102 (WAIT_TIMEOUT))
goto *lb_mainloop
}
*lb_onexit
; 終了時(ウィンドウ右上の×ボタンが押されたとき)の処理
m = {"
サブプロセスはまだ終了していません。
メインプロセスを終了しますか?
(この場合サブプロセスは終了されません)
"}
dialog m, 2
if stat == 6 { ; "YES" が押された場合
goto *lb_quit
} else { ; "NO" が押された場合
goto *lb_mainloop
}
*lb_quit
; 終了処理(プロセスハンドルのクローズ)
dllproc "CloseHandle", hprocess, 1, D_KERNEL
hprocess = 0
end