2009-11-05 115 views
7

是否可以等待Windows中的子进程启动的所有进程?我无法修改孩子或孙子的过程。在Windows中等待孙子进程

具体来说,这是我想要做的。我的进程启动uninstallA.exe。进程uninistallA.exe启动uninstallB.exe并立即退出,uninstallB.exe运行一段时间。我想等uninstallB.exe退出,以便我可以知道卸载何时完成。

回答

9

CreateJobObject创建一个作业对象。使用CreateProcess以暂停状态启动UninstallA.exe。使用AssignProcessToJobObject将新流程分配给作业对象。通过ResumeThreadCreateProcess获得的线程句柄上启动UninstallA.exe。

然后困难的部分:等待作业对象完成其执行。不幸的是,这比任何人合理希望的要复杂得多。基本思想是创建I/O完成端口,然后创建对象对象,将其与I/O完成端口关联,最后等待I/O完成端口(通过GetQueuedCompletionStatus获取其状态)。 Raymond Chen在他的blog上进行了演示(并解释了这是如何发生的)。

+4

谢谢。它看起来像一个简单的WaitForSingleObject不会告诉我所有进程何时退出,但它看起来像我可以创建一个IO完成端口并等待JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO。 – thudbang 2009-11-06 22:20:43

+0

Raymond Chen在他的博客中提到:[我如何等到工作中的所有流程都退出?](https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743) – 2017-09-18 21:35:27

0

使用named mutex

+0

我不认为我可以那样做。我无法修改子进程。 – thudbang 2009-11-05 22:35:49

+0

是的,那将无法工作,那么。 – Cheeso 2009-11-05 23:39:09

0

一种可能是安装Cygwin,然后使用ps命令来监控孙子退出

1

没有一个通用的方法来等待所有的孙子,但对您的特定情况下,你可能能够破解的东西一起。你知道你正在寻找一个特定的流程实例。我首先会等待uninstallA.exe退出(使用WaitForSingleObject),因为那时您知道uninstallB.exe已启动。然后使用PSAPI中的EnumProcesses和GetProcessImageFileName来查找正在运行的uninstallB.exe实例。如果你没有找到它,你就会知道它已经完成了,否则你可以等待它。

另外一个复杂之处在于,如果你需要支持的Windows版本早于XP不能使用GetProcessImageFileName,而Windows NT中不能使用PSAPI的。对于Windows 2000,您可以使用GetModuleFileNameEx,但它有一些警告意味着它可能会失败(检查文档)。如果您必须支持NT,请查看Toolhelp32。

是的,这是超级丑陋的。

+1

无论如何,你可能会考虑使用Toolhelp32,因为它提供了每个进程的父(产卵)ID,而CreateProcess()也返回了一个进程ID。您应该能够启动uninstallA.exe,获取其进程ID,然后使用ToolHelp32查找由相同进程ID启动的所有进程。在关闭由CreateProcess()返回的进程句柄之前,操作系统无法重用uninstallA的进程ID。 – 2009-11-06 01:02:51

1

这里的,虽然并非十全十美,如果由于某种原因,你不能使用作业对象,可能是有用的技术。这个想法是创建一个匿名管道,让子进程继承管道的写入端。

通常,孙子进程也将继承管的写入结束。特别是,由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; 

}