2009-09-21 6 views
14

Tengo una aplicación A que me gustaría poder invocar otros procesos arbitrarios según lo especificado por un usuario en un archivo de configuración.¿Ejecutar subprocesos arbitrarios en Windows y todavía terminar limpiamente?

La secuencia de comandos B de lotes es uno de los procesos que un usuario desearía invocar por A. B configura algunas variables de entorno, muestra algunos mensajes e invoca un compilador C para hacer algún trabajo.

¿Proporciona Windows una forma estándar para que los procesos arbitrarios terminen de forma limpia? Supongamos que A se ejecuta en una consola y recibe un CTRL + C. ¿Puede pasar esto a B y C? Supongamos que A se ejecuta en una ventana y el usuario intenta cerrar la ventana, ¿puede cancelar B y C?

TerminateProcess es una opción, pero no muy buena. Si A usa TerminateProcess en B, C sigue ejecutándose. Esto podría causar problemas desagradables si C es de larga ejecución, ya que podríamos iniciar otra instancia de C para operar en los mismos archivos mientras la primera instancia de C aún está funcionando secretamente. Además, TerminateProcess no da como resultado una salida limpia.

GenerateConsoleCtrlEvent suena bien, y podría funcionar cuando todo se ejecuta en una consola, pero la documentación dice que solo puede enviar CTRL + C a su propia consola y no ayudaría si A se ejecutara en una ventana.

¿Hay algún equivalente a SIGINT en Windows? Me encantaría encontrar un artículo como este: http://www.cons.org/cracauer/sigint.html para Windows.

+1

+1 para una buena pregunta. Es curioso que no haya habido respuestas ... No conozco un mecanismo de señal en Win32. Cuando necesité notificar los procesos secundarios, publiqué un mensaje WM_CLOSE para las aplicaciones GUI. Para las aplicaciones de consola, las creé adjuntando flujos de entrada y salida a la aplicación principal y cuando quise terminarlas, simplemente cerré el flujo de entrada. En cuanto a hijos de niños, etc., confié en mis hijos directos para limpiar sus dependencias :). –

Respuesta

9

Creo que llegué un poco tarde a esta pregunta, pero escribiré algo de todos modos para cualquiera que tenga el mismo problema.

Mi problema es similar en el sentido de que me gustaría que mi aplicación sea una aplicación GUI pero los procesos ejecutados deben ejecutarse en segundo plano sin ninguna ventana de consola interactiva adjunta.

Pude resolver esto usando GenerateConsoleCtrlEvent(). La parte difícil es simplemente que la documentación no es realmente clara sobre cómo se puede utilizar y las dificultades con ella.

Mi solución se basa en lo que se describe here. Pero eso tampoco explicaba todos los detalles, así que aquí están los detalles sobre cómo hacerlo funcionar.

  1. Cree una nueva aplicación auxiliar "Helper.exe". Esta aplicación se ubicará entre su aplicación (principal) y el proceso secundario que desea que pueda cerrar. También creará el proceso hijo real. Debe tener este proceso de "intermediario" o GenerateConsoleCtrlEvent() fallará.

  2. Utilice algún tipo de mecanismo de IPC para comunicar desde el proceso principal al auxiliar que el ayudante debe cerrar el proceso secundario. Cuando el ayudante obtiene este evento llama a "GenerateConsoleCtrlEvent (CTRL_BREAK, 0)" que se cierra solo y el proceso secundario. Usé yo mismo un objeto de evento que el padre completa cuando quiere cancelar el proceso hijo.

para crear su helper.exe crear con CREATE_NO_WINDOW y CREATE_NEW_PROCESS_GROUP. Y al crear el proceso hijo, créelo sin flags (0), lo que significa que derivará la consola de su principal. Si no lo haces, ignorará el evento.

Es muy importante que cada paso se haga así. He estado probando diferentes tipos de combinaciones, pero esta combinación es la única que funciona. No puedes enviar un evento CTRL_C.Devolverá el éxito pero será ignorado por el proceso. CTRL_BREAK es el único que funciona. Realmente no importa ya que ambos llamarán a ExitProcess() al final.

Tampoco puede llamar a GenerateConsoleCtrlEvent() con un ID de grupo de proceso de la Id. De proceso hijo directamente permitiendo que el proceso de ayuda continúe viviendo. Esto también fallará

Pasé todo el día intentando que esto funcionara. Esta solución funciona para mí, pero si alguien tiene algo más para agregar, hazlo. Fui al otro lado de la red encontrando a mucha gente con problemas similares pero sin una solución definitiva al problema. Cómo funciona GenerateConsoleCtrlEvent() también es un poco raro, así que si alguien conoce más detalles, por favor comparte.

+3

Tenga en cuenta que no hay garantía de que los procesos B o C salgan limpiamente al recibir Ctrl + C. –

+0

Aunque no hay garantía de que una aplicación remota responda a Ctrl + C, si se da por hecho que la aplicación de destino termina terminantemente en respuesta a ctrl + c, entonces es importante conocer este método, que puede causar cualquier aplicación conocida para terminar limpiamente. – Triynko

+0

La respuesta aceptada debería ser la de @KindDragon –

8

Como @Shakta dijo GenerateConsoleCtrlEvent() es muy complicado, pero puede enviar Ctrl + C sin proceso de ayuda.

void SendControlC(int pid) 
{ 
    AttachConsole(pid); // attach to process console 
    SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app 
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event 
} 
+0

¡Esto fue muy útil! Solo falta una cosa: debe llamar a FreeConsole antes de usar AttachConsole, de lo contrario recibirá ERROR_ACCESS_DENIED (5) ya que el proceso de llamada ya está conectado a una consola. – Yael

+0

Esta es la única respuesta a través de una docena de preguntas que ha tenido CUALQUIER éxito para mí, ¡gracias! –

Cuestiones relacionadas