さて、実際のスクリプト書いて見ましょう。
今回はメインプログラムからメモ帳を実行し、メモ帳が閉じられたらメインプログラムが実行を再開するようにしてみましょう。
#uselib "kernel32.dll" #func CloseHandle "CloseHandle" int #func CreateProcess "CreateProcessA" int,int,int,int,int,int,int,int,int,int #func WaitForSingleObject "WaitForSingleObject" int,int #define INFINITE 0xFFFFFFFF ; 起動する実行可能ファイル名(必要ならコマンドラインパラメータも含める) cmd = "notepad.exe" ; STARTUPINFO構造体を設定 dim stinfo, 17 stinfo.0 = 68 ; 構造体サイズ(cbメンバ) ; PROCESS_INFOMATION構造体を設定 dim prinfo, 4 ; 新しいプロセスを起動 CreateProcess 0,varptr(cmd),0,0,0,0,0,0,varptr(stinfo),varptr(prinfo) if (stat == 0) : dialog "起動失敗" : end hProcess = prinfo.0 ; プロセスのハンドル hThread = prinfo.1 ; プライマリスレッドのハンドル ; プライマリスレッドのハンドルは直ちにクローズ CloseHandle hThread hThread = 0 ; サブプロセスの終了を待機する WaitForSingleObject hProcess, INFINITE ; プロセスハンドルをクローズ CloseHandle hProcess hProcess = 0 dialog "サブプロセスが終了しました。" end
前回も述べましたが、普通に起動させるのであれば、STARTUPINFO構造体には特に何も指定する必要はありません。cbメンバに構造体サイズ68を格納し、それ以外のメンバはすべて0になるように指定します。
今回はCreateProcess関数の第2引数に実行ファイル名「notepad.exe」を指定しています。なぜ第1引数に指定しないのかというと、第1引数に実行ファイル名を指定する場合は、フルパス指定か、さもなくばカレントディレクトリの実行ファイルを指定することしかできないためです。第2引数に(必要ならコマンドラインパラメータも添えて)指定すると、実行ファイルをカレントディレクトリのほかにWindowsディレクトリやシステムディレクトリからも検索してくれます。また、拡張子「.exe」を省略することもできます。メモ帳はWindowsディレクトリ内にあるので、こうしなければならないのです。
CreateProcess関数を呼び出したら、必要のないプライマリスレッドのハンドルはすぐにCloseHandle関数を使ってクローズしておきます。その後、プロセスのハンドルを引数としてWaitForSingleObject関数を呼び出します。メモ帳が終了するまではここで処理が中断されます。処理が再開されたら、プロセスのハンドルをクローズします。
上のサンプルを実行させてみると分かりますが、サブプロセスが終了しているのを待機している間は、メインプロセスのウィンドウはまったく反応しません。WaitForSingleObject関数の待ち時間として0xFFFFFFFF (INFINITE) を指定しているため、サブプロセスが終了するまで無限に待ち続けます。その間、ウィンドウの移動やサイズ変更、プロセスの終了もできないというのは、少々困ります。
これらの問題を解決するには、WaitForSingleObject関数の待ち時間として、INFINITE以外の適切なタイムアウト値(あるいは0)を指定します。そして、プロセスの終了を待機するループを作成し、関数が戻り値0 (WAIT_OBJECT_0) を返すまで繰り返し呼び出します。
この場合には、サブプロセスが終了したかどうかをループ中で監視をするのですが、ウィンドウの処理に対応するためにはこのループ中にwaitまたはawaitを実行する必要があります。この方法により、プロセス終了を監視しながら他の処理を平行して行うことも可能となります。
ここでは、上の2つの方法のうち、タイムアウト時間を指定する方法のみを紹介しておきましょう。
#uselib "kernel32.dll" #func CloseHandle "CloseHandle" int #func CreateProcess "CreateProcessA" int,int,int,int,int,int,int,int,int,int #func WaitForSingleObject "WaitForSingleObject" int,int #define WAIT_OBJECT_0 0 ; 起動する実行可能ファイル名(必要ならコマンドラインパラメータも含める) cmd = "notepad.exe" ; STARTUPINFO構造体を設定 dim stinfo, 17 stinfo.0 = 68 ; 構造体サイズ(cbメンバ) ; PROCESS_INFOMATION構造体を設定 dim prinfo, 4 ; 新しいプロセスを起動 CreateProcess 0,varptr(cmd),0,0,0,0,0,0,varptr(stinfo),varptr(prinfo) if (stat == 0) : dialog "起動失敗" : end hProcess = prinfo.0 ; プロセスのハンドル hThread = prinfo.1 ; プライマリスレッドのハンドル ; プライマリスレッドのハンドルは直ちにクローズ CloseHandle hThread hThread = 0 ; HSPプログラム終了時にサブルーチンジャンプする onexit gosub *lb_onexit *lb_mainloop ; HSPウィンドウへの処理に対応するためには wait または await が必要 await 0 ; サブプログラムの終了を待機する (タイムアウト時間:100ミリ秒) WaitForSingleObject hProcess, 100 if (stat == WAIT_OBJECT_0) { ; プロセスがシグナル状態になった(サブプログラムが終了した)場合は終了 dialog "サブプロセスが終了しました" goto *lb_quit } goto *lb_mainloop *lb_onexit ; 終了時(ウィンドウ右上の×ボタンが押されたとき)の処理 m = {" サブプロセスはまだ終了していません。 メインプロセスを終了しますか? (この場合サブプロセスは終了されません) "} dialog m, 2 if stat == 6 { ; "YES" が押された場合は終了 goto *lb_quit } return *lb_quit ; 終了処理(プロセスハンドルのクローズ) CloseHandle hProcess hprocess = 0 end