ここではいくつかの理由で、ジョブオブジェクトを使用できない場合は絶対確実ではないが、役立つことができ、技術があります。アイデアは、匿名パイプを作成し、子プロセスがパイプの書き込み側のハンドルを継承するようにすることです。
典型的には、孫プロセスもパイプの書き込み終了を継承します。特に、cmd.exe
によって(例えば、バッチファイルから)起動されたプロセスはハンドルを継承する。子プロセスが終了した後は
、親プロセスはパイプの書き込み側にそのハンドルを閉じ、その後、パイプから読み込もうとします。誰もパイプに書き込んでいないので、読み取り操作は無期限にブロックされます。 (もちろん、孫を待っている間も何かをやりたいのであれば、スレッドや非同期I/Oを使うことができます)。
パイプの書き込み側の最後のハンドルが閉じているときパイプの書き込み終了は自動的に破棄されます。これによりパイプが破壊され、読み取り操作が完了し、ERROR_BROKEN_PIPEエラーが報告されます。
私はこのコード(および同じコードの以前のバージョン)を数年間生産中に使用してきました。
// pwatch.c
//
// Written in 2011 by Harry Johnston, University of Waikato, New Zealand.
// This code has been placed in the public domain. It may be freely
// used, modified, and distributed. However it is provided with no
// warranty, either express or implied.
//
// Launches a process with an inherited pipe handle,
// and doesn't exit until (a) the process has exited
// and (b) all instances of the pipe handle have been closed.
//
// This effectively waits for any child processes to exit,
// PROVIDED the child processes were created with handle
// inheritance enabled. This is usually but not always
// true.
//
// In particular if you launch a command shell (cmd.exe)
// any commands launched from that command shell will be
// waited on.
#include <windows.h>
#include <stdio.h>
void error(const wchar_t * message, DWORD err) {
wchar_t msg[512];
swprintf_s(msg, sizeof(msg)/sizeof(*msg), message, err);
printf("pwatch: %ws\n", msg);
MessageBox(NULL, msg, L"Error in pwatch utility", MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
ExitProcess(err);
}
int main(int argc, char ** argv) {
LPWSTR lpCmdLine = GetCommandLine();
wchar_t ch;
DWORD dw, returncode;
HANDLE piperead, pipewrite;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
char buffer[1];
while (ch = *(lpCmdLine++)) {
if (ch == '"') while (ch = *(lpCmdLine++)) if (ch == '"') break;
if (ch == ' ') break;
}
while (*lpCmdLine == ' ') lpCmdLine++;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&piperead, &pipewrite, &sa, 1)) error(L"Unable to create pipes: %u", GetLastError());
GetStartupInfo(&si);
if (!CreateProcess(NULL, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
error(L"Error %u creating process.", GetLastError());
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) error(L"Error %u waiting for process.", GetLastError());
if (!GetExitCodeProcess(pi.hProcess, &returncode)) error(L"Error %u getting exit code.", GetLastError());
CloseHandle(pipewrite);
if (ReadFile(piperead, buffer, 1, &dw, NULL)) {
error(L"Unexpected data received from pipe; bug in application being watched?", ERROR_INVALID_HANDLE);
}
dw = GetLastError();
if (dw != ERROR_BROKEN_PIPE) error(L"Unexpected error %u reading from pipe.", dw);
return returncode;
}
ありがとうございます。シンプルなWaitForSingleObjectのように見えますが、すべてのプロセスが終了したときにはわかりませんが、IO完了ポートを作成してJOB_OBJECT_MSG_ACTIVE_PROCESS_ZEROを待つことができます。 – thudbang
Raymond Chenは彼のブログでそれをカバーしています:[仕事のすべてのプロセスが終了するまでどのように待つのですか?](https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743) –