2010-01-18 18 views
6

Sto lavorando con C# e non riesco a capire come leggere stdout in modo asincrono da un processo figlio. Quello che voglio fare è creare un processo figlio che esegua un'applicazione e quindi presenti ciò che viene ricevuto dallo stdout di quel processo in una casella di testo. Devo vedere immediatamente tutti i caratteri di output del processo figlio e non posso aspettare il completamento di una riga, quindi non penso che l'evento Process.OutputDataReceived soddisfi il mio scopo. Puoi dirmi un modo sano per realizzare questo?C# Leggi lo stdout del processo figlio in modo asincrono

Ho provato a chiamare Process.StandardOutput.BaseStream.BeginRead() e passare una funzione di richiamata ad esso, ma in questa funzione di richiamata ricevo un'eccezione da Process.StandardOutput.BaseStream.EndRead().

Il mio codice è simile al seguente (il processo figlio è un motore di script - abbreviato "SE" -. Per verificare la funzionalità di un dispositivo esterno Gli script vengono eseguiti in sequenza e ogni script richiede un'istanza dell'applicazione SE)

private bool startScript() 
{ 
    // Starts the currently indexed script 

    if (null != scriptList) 
    { 

    if (0 == scriptList.Count || scriptListIndexer == scriptList.Count) 
    { 
     // If indexer equals list count then there are no active scripts in 
     // the list or the last active script in the list has just finished 
     return false;        // ## RETURN ## 
    } 
    if (ScriptExecutionState.RUNNING == scriptList[scriptListIndexer].executionState) 
    { 
     return false;        // ## RETURN ## 
    } 

    if (0 == SE_Path.Length) 
    { 
     return false;        // ## RETURN ## 
    } 

    SE_Process = new Process(); 
    SE_Process.StartInfo.FileName = SE_Path; 
    SE_Process.StartInfo.CreateNoWindow = true; 
    SE_Process.StartInfo.UseShellExecute = false; 
    SE_Process.StartInfo.RedirectStandardError = true; 
    SE_Process.StartInfo.RedirectStandardOutput = true; 
    SE_Process.EnableRaisingEvents = true; 
    SE_Process.StartInfo.Arguments = scriptList[scriptListIndexer].getParameterString(); 

    // Subscribe to process exit event 
    SE_Process.Exited += new EventHandler(SE_Process_Exited); 

    try 
    { 
     if (SE_Process.Start()) 
     { 
     // Do stuff 
     } 
     else 
     { 
     // Do stuff 
     } 
    } 
    catch (Exception exc) 
    { 
     // Do stuff 
    } 

    // Assign 'read_SE_StdOut()' as call-back for the event of stdout-data from SE 
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null); 

    return true;          // ## RETURN ## 

    } 
    else 
    { 
    return false;          // ## RETURN ## 
    } 
} 

private void read_SE_StdOut(IAsyncResult result) 
{ 
    try 
    { 
    int bytesRead = SE_Process.StandardOutput.BaseStream.EndRead(result); // <-- Throws exceptions 

    if (0 != bytesRead) 
    { 
     // Write the received data in output textbox 
     ... 
    } 

    // Reset the callback 
    SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE,  read_SE_StdOut, null); 
    } 
    catch (Exception exc) 
    { 
    // Do stuff 
    } 
} 

void SE_Process_Exited(object sender, EventArgs e) 
{ 

    // Keep track of whether or not the next script shall be started 
    bool continueSession = false; 

    switch (SE_Process.ExitCode) 
    { 
    case 0: // PASS 
    { 
     // Do stuff 
    } 

    ... 

    } 

    SE_Process.Dispose(); // TODO: Is it necessary to dispose of the process? 

    if (continueSession) 
    { 
    ts_incrementScriptListIndexer(); 

    if (scriptListIndexer == scriptList.Count) 
    { 
     // Last script has finished, reset the indexer and re-enable 
     // 'scriptListView' 
     ... 
    } 
    else 
    { 
     if (!startScript()) 
     { 
     // Do stuff 
     } 
    } 
    } 
    else 
    { 
    ts_resetScriptListIndexer(); 
    threadSafeEnableScriptListView(); 
    } 
} 

quello che succede è che dopo un processo SE termina ottengo un'eccezione di tipo InvalidOperationException che dice

StandardOut non è stato reindirizzato o il processo non è ancora iniziata.

dalla chiamata a SE_Process.StandardOutput.BaseStream.EndRead(). Non capisco perché, perché ho impostato SE_Process.StartInfo.RedirectStandardOutput prima di iniziare ogni nuovo processo. Mi sembra che se il flusso stdout di un processo uscito chiama la mia funzione read_SE_StdOut() dopo che il processo è stato eliminato, è possibile?

Grazie per la lettura!

+0

Potrebbe interessarti la libreria [MedallionShell] (https://github.com/madelson/MedallionShell), che semplifica la gestione dei flussi di processo io, in particolare le operazioni asincrone – ChaseMedallion

risposta

2

L'eccezione che si ottiene è normale. Una chiamata BeginRead() non può mai avere successo: l'ultima, subito dopo la conclusione del processo. Normalmente eviterai di chiamare lo BeginRead() se sai che il processo è completato in modo da non ottenere l'eccezione. Tuttavia, raramente lo sapresti. Prendi l'eccezione. Oppure usa BeginOutputReadLine() invece, lo prenderà per te.

Sto indovinando che si reindirizza anche stderr e che lo strumento lo utilizza per l'output "X". Non v'è alcun modo per mantenere l'uscita sia stderr e stdout sincronizzato dopo che è tamponato e reindirizzato.

+0

Vedo, gestirò solo l'eccezione quindi . Riguardo l'ordine disturbato dei personaggi; anche le X: es sono messe su stdout quindi non penso che questo spieghi perché sono presentate fuori ordine. Ti farò questa domanda rimangono senza risposta, perché il mio problema più grande è la visualizzazione non corretta dell'uscita (ho pensato che sarebbe stato messo in relazione all'eccezione in qualche modo, ma forse non lo è). La ringrazio per la risposta! – jokki

+0

Il mio problema con la visualizzazione disordinato di caratteri ricevuti non si verifica più, devo aver fissato per caso :) Ho rimosso quella parte della domanda di cui sopra e impostare la domanda a "risposto". Grazie ancora! – jokki

Problemi correlati