2009-11-05 10 views
7

¿Es posible esperar todos los procesos iniciados por un proceso secundario en Windows? No puedo modificar los procesos de hijo o nieto.Esperando procesos de nieto en Windows

Específicamente, esto es lo que quiero hacer. Mi proceso inicia uninstallA.exe. El proceso uninistallA.exe inicia uninstallB.exe e inmediatamente se cierra, y uninstallB.exe se ejecuta por un tiempo. Me gustaría esperar a que uninstallB.exe salga para poder saber cuándo finalizó la desinstalación.

Respuesta

9

Crear un objeto de trabajo con CreateJobObject. Use CreateProcess para iniciar UninstallA.exe en un estado suspendido. Asigne ese nuevo proceso a su objeto de trabajo con AssignProcessToJobObject. Inicie UninstallA.exe ejecutándose llamando al ResumeThread en el manejador del hilo que obtuvo de CreateProcess.

Luego, la parte difícil: espere a que el objeto de trabajo complete su ejecución. Desafortunadamente, esto es bastante más complejo de lo que cualquiera podría esperar. La idea básica es crear un puerto de E/S de finalización, luego crear el objeto objeto, asociarlo con el puerto de finalización de E/S y finalmente esperar en el puerto de finalización de E/S (obteniendo su estado con GetQueuedCompletionStatus). Raymond Chen tiene una demostración (y una explicación de cómo sucedió esto) en su blog.

+4

Gracias. Parece que un simple WaitForSingleObject no me dirá cuándo se han cerrado todos los procesos, pero parece que puedo crear un puerto de finalización de IO y esperar a JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO. – thudbang

+0

Raymond Chen lo cubre en su blog: [¿Cómo espero hasta que se hayan cerrado todos los procesos en un trabajo?] (Https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743) –

0

Utilice un named mutex.

+0

No creo que pueda hacer eso. No puedo modificar los procesos secundarios. – thudbang

+0

sí, eso no funcionará, entonces. – Cheeso

0

Una posibilidad es instalar Cygwin y luego utilizar el comando ps para ver para el nieto para salir

1

No hay una forma genérica que esperar a que todos los nietos, pero para su caso específico que puede ser capaz de hackear algo juntos. Usted sabe que está buscando una instancia de proceso específica. Primero esperaría que uninstallA.exe salga (usando WaitForSingleObject) porque en ese momento ya sabes que se ha iniciado uninstallB.exe. A continuación, utilice EnumProcesses y GetProcessImageFileName de PSAPI para buscar la instancia de ejecución uninstallB.exe. Si no lo encuentras, sabes que ya ha terminado, de lo contrario puedes esperarlo.

Una complicación adicional es que si necesita admitir versiones de Windows anteriores a XP no puede usar GetProcessImageFileName, y para Windows NT no puede usar PSAPI en absoluto. Para Windows 2000 puede usar GetModuleFileNameEx, pero tiene algunas advertencias que significan que a veces puede fallar (consultar documentos). Si tiene que admitir NT, busque Toolhelp32.

Sí, esto es súper feo.

+1

Puede considerar el uso de Toolhelp32 de todos modos, simplemente porque proporciona el ID padre (desove) de cada proceso, y CreateProcess() también devuelve un ID de proceso. Debería poder iniciar uninstallA.exe, obtener su ID de proceso y luego usar ToolHelp32 para buscar todos los procesos que fueron iniciados por ese mismo ID de proceso. Hasta que cierre el identificador de proceso devuelto por CreateProcess(), el sistema operativo no puede reutilizar el ID de proceso de uninstallA. –

1

Esta es una técnica que, aunque no es infalible, puede ser útil si por algún motivo no puede usar un objeto de trabajo. La idea es crear un conducto anónimo y dejar que el proceso secundario herede el controlador hasta el final de escritura del conducto.

Normalmente, los procesos de nieto también heredarán el final de escritura de la tubería. En particular, los procesos iniciados por cmd.exe (por ejemplo, desde un archivo por lotes) heredarán identificadores.

Una vez que el proceso secundario ha salido, el proceso padre cierra su manejador hasta el final de escritura de la tubería, y luego intenta leer desde la tubería. Como nadie está escribiendo en la tubería, la operación de lectura se bloqueará indefinidamente. (Por supuesto, puede usar subprocesos o E/S asíncronas si desea seguir haciendo cosas mientras espera a los nietos.)

Cuando (y solo cuando) se cierra el último identificador del extremo de escritura de la tubería, el el final de escritura de la tubería se destruye automáticamente. Esto rompe el conducto y la operación de lectura finaliza e informa un error ERROR_BROKEN_PIPE.

He estado usando este código (y versiones anteriores del mismo código) en producción durante varios años.

// 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; 

} 
Cuestiones relacionadas