single.php

OpenProcessで取得したプロセスIDをWaitForSingleObjectで監視する

Win32APIのWaitForSingleObject関数を使って、アプリケーションの終了を監視する際に、OpenProcessで取得したIDを使う場合に、ハマったので備忘録的な記事として投稿します。

アプリケーションの終了を監視

「別のプログラムを起動して、その終了を待って処理を進めたい」と言う要望は結構あります。実際に実装しようと思うと、

無限ループを作って、繰り返し処理で終了を監視する

とか

タイマーイベントを作って、定期的に終了を監視する

ような実装が考えられます。無限ループで監視する方法は一見簡単ですがリソースに負荷が掛かったり、本当に無限になってしまい監視ループから抜け出せなくなるリスクが伴います。

タイマーイベントも、ウィンドウがある場合には有効なイベントなのでウィンドウレスなアプリケーションや、サービスなどでは有効な実装方法になりません。

そこで活躍するのが「WaitForSingleObject」関数です。この関数は引数で渡されたプロセスIDを監視して、終了時に関数から戻ってくるという関数です。

CreateProcessと一緒に使う

ネットで検索すると、CreateProcessで作成したプロセスを監視するコードが出てきます。例えばこんな感じ

PROCESS_INFORMATION procInfo;
STARTUPINFO startUpInfo;
ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startUpInfo, sizeof(STARTUPINFO));
startUpInfo.cb = sizeof(STARTUPINFO);
CString sCommandLine = "C:\Windows\Notepad.exe";
CreateProcess(
    NULL,
    sCommandLine.GetBuffer(),
    0,
    0,
    FALSE,
    CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS,
    0,
    0,
    &startUpInfo,
    &procInfo
);

sCommandLine.ReleaseBuffer();

::WaitForSingleObject(procInfo.hProcess, INFINITE);
::CloseHandle(procInfo.hProcess);

実行するとメモ帳が開いて、WaitForSingleObject関数で停止し、メモ帳を閉じると処理が進みます。

OpenProcessと一緒に使う

新しくプロセスを作る(アプリケーションを実行する)他に、既に実行中のアプリケーションでも同じようにWaitForSingleObject関数を使って、アプリケーションの終了を監視することができます。

コードにすると、こんな感じ

HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_VM_READ, FALSE, dwProcId);
::WaitForSingleObject(hProcess, INFINITE);
::CloseHandle(hProcess);

CreateProcessの場合と比較すると、かなりコードが簡単になりますが、OpenProcessで利用するプロセスID(コードで言うdwProcId)を取得するのに苦労します。この辺りは、この記事では割愛します。

また、OpenProcess関数の第一引数で指定するパラメタにも注意が必要になります。

コード例では[SYNCHRONIZE]を指定していますが、この値が含まれている必要があります。

OpenProcessのリファレンスを見てみると、こんな一覧になります。

パラメタ一覧

説明
PROCESS_ALL_ACCESS利用可能な範囲で、プロセスオブジェクトに対するすべてのアクセス権を指定します。
PROCESS_CREATE_PROCESS内部で使います。
PROCESS_CREATE_THREADプロセス内にスレッドを作成するために、CreateRemoteThread 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_DUP_HANDLEハンドルを複製するために、 関数が複製元または複製先としてこのプロセスのハンドルを使うことを認めます。
PROCESS_QUERY_INFORMATIONプロセスオブジェクトから情報を読み取るために、GetExitCodeProcess 関数と GetPriorityClass 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_SET_QUOTAメモリの上限(クォータ)を設定するために、AssignProcessToJobObject 関数と SetProcessWorkingSetSize 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_SET_INFORMATIONこのプロセスの優先順位クラスを設定するために、SetPriorityClass 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_TERMINATEこのプロセスを終了するために、TerminateProcess 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_VM_OPERATION このプロセスの仮想メモリを変更するために、 関数、または 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_VM_READこのプロセスの仮想メモリの内容を読み取るために、 関数がこのプロセスのハンドルを使うことを認めます。
PROCESS_VM_WRITEこのプロセスの仮想メモリへの書き込みを行うために、 関数がこのプロセスのハンドルを使うことを認めます。
SYNCHRONIZEこのプロセスが終了するのを待つために、(待機関数)がこのプロセスのハンドルを使うことを認めます。

CreateProcess関数の場合には、気にすることはないですが、OpenProcess関数の場合にはWaitForSingleObject関数で利用できるように、取得時にパラメタに注意が必要になります。

短いですが、今回はこんなところで終わります。

エクスプローラーの表示が遅くて困っている人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です