2009-09-21 9 views
14

Ho un'applicazione A che mi piacerebbe poter richiamare altri processi arbitrari come specificato da un utente in un file di configurazione.Eseguire sottoprocessi arbitrari su Windows e terminare ancora in modo pulito?

Lo script B di batch è un processo di questo tipo che un utente vorrebbe essere richiamato da A. B imposta alcune variabili di ambiente, mostra alcuni messaggi e invoca un compilatore C per eseguire alcuni lavori.

Windows fornisce un modo standard per terminare in modo pulito i processi arbitrari? Supponiamo che A venga eseguito in una console e riceve un CTRL + C. Può passare questo a B e C? Supponiamo che A venga eseguito in una finestra e che l'utente provi a chiudere la finestra, può annullare B e C?

TerminateProcess è un'opzione, ma non molto buona. Se A utilizza TerminateProcess su B, C continua a essere in esecuzione. Questo potrebbe causare problemi se C è di lunga durata, dato che potremmo avviare un'altra istanza di C per operare sugli stessi file mentre la prima istanza di C è ancora segretamente al lavoro. Inoltre, TerminateProcess non risulta in un'uscita pulita.

GenerateConsoleCtrlEvent suona bene, e potrebbe funzionare quando tutto è in esecuzione in una console, ma la documentazione dice che è possibile inviare CTRL + C alla propria console, quindi non sarebbe di aiuto se A fosse in esecuzione in una finestra.

C'è qualche equivalente a SIGINT su Windows? Mi piacerebbe trovare un articolo come questo: http://www.cons.org/cracauer/sigint.html per Windows.

+1

+1 per una buona domanda. Divertente non ci sono state risposte ... Non sono a conoscenza di un meccanismo simile a un segnale in Win32. Quando ho avuto bisogno di notificare i processi figli ho postato un messaggio WM_CLOSE nelle app della GUI. Per le applicazioni della console li ho creati collegando i flussi di input e output all'applicazione principale e quando volevo terminarli ho semplicemente chiuso il flusso di input. Per quanto riguarda i bambini, ecc., Ho fatto affidamento sui miei figli diretti per ripulire le loro dipendenze :). –

risposta

9

Credo di essere un po 'in ritardo su questa domanda, ma scriverò comunque qualcosa per chiunque abbia lo stesso problema.

Il mio problema è simile in quanto mi piacerebbe che la mia applicazione fosse un'applicazione GUI, ma i processi eseguiti dovrebbero essere eseguiti in background senza alcuna finestra della console interattiva collegata.

Sono riuscito a risolvere questo utilizzando GenerateConsoleCtrlEvent(). La parte difficile è solo che la documentazione non è chiara su come esattamente può essere utilizzato e le insidie ​​con esso.

La mia soluzione è basata su ciò che è descritto here. Ma non ha nemmeno spiegato tutti i dettagli, quindi ecco i dettagli su come farlo funzionare.

  1. Creare una nuova applicazione di supporto "Helper.exe". Questa applicazione si posizionerà tra l'applicazione (genitore) e il processo figlio che si desidera poter chiudere. Creerà anche il processo figlio effettivo. È necessario disporre di questo processo "middle man" o GenerateConsoleCtrlEvent() non riuscirà.

  2. Utilizzare un tipo di meccanismo IPC per comunicare dal processo padre a quello di supporto che l'helper deve chiudere il processo figlio. Quando l'helper riceve questo evento, chiama "GenerateConsoleCtrlEvent (CTRL_BREAK, 0)" che chiude se stesso e il processo figlio. Ho usato un oggetto evento per questo me stesso che il genitore completa quando vuole cancellare il processo figlio.

Per creare la helper.exe crearla con CREATE_NO_WINDOW e CREATE_NEW_PROCESS_GROUP. E quando crea il processo figlio lo crea senza flag (0), il che significa che deriverà la console dal suo genitore. In caso contrario, ignorerà l'evento.

È molto importante che ogni passaggio venga eseguito in questo modo. Ho provato diversi tipi di combinazioni, ma questa combinazione è l'unica che funziona. Non puoi inviare un evento CTRL_C.Restituirà successo ma verrà ignorato dal processo. CTRL_BREAK è l'unico che funziona. Non ha molta importanza dal momento che entrambi chiameranno ExitProcess() alla fine.

Non è inoltre possibile chiamare GenerateConsoleCtrlEvent() con un ID di processo groupd dell'ID processo figlio che consente direttamente al processo di supporto di continuare a vivere. Anche questo fallirà.

Ho passato un'intera giornata a cercare di farlo funzionare. Questa soluzione funziona per me, ma se qualcuno ha qualcos'altro da aggiungere, per favore fallo. Sono andato a tutti gli altri nella rete trovando molte persone con problemi simili ma senza una soluzione definitiva al problema. Il funzionamento di GenerateConsoleCtrlEvent() è anche un po 'strano, quindi se qualcuno conosce più dettagli, per favore condividi.

+3

Si noti che non è garantito che i processi B o C usciranno in modo pulito al ricevimento di Ctrl + C. –

+0

Anche se non c'è alcuna garanzia che un'applicazione abrary risponda a Ctrl + C, se è dato che l'applicazione di destinazione è in realtà nota per terminare in modo pulito in risposta a ctrl + c, allora è importante conoscere questo metodo, che può causare qualsiasi applicazione nota per terminare in modo pulito. – Triynko

+0

La risposta accettata dovrebbe essere quella di @KindDragon –

8

Come ha detto @Shakta GenerateConsoleCtrlEvent() è molto difficile, ma è possibile inviare Ctrl + C senza processo di supporto.

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

Questo è stato molto utile! Manca solo una cosa: devi chiamare FreeConsole prima di utilizzare AttachConsole, altrimenti riceverai ERROR_ACCESS_DENIED (5) poiché il processo chiamante è già collegato a una console. – Yael

+0

Questa è l'unica risposta tra una dozzina di domande che ha avuto QUALSIASI successo per me, grazie! –

Problemi correlati