2010-01-18 19 views
6

Estoy trabajando con C# y tengo problemas para entender cómo leer stdout de forma asincrónica desde un proceso secundario. Lo que quiero hacer es crear un proceso hijo que ejecute una aplicación y luego presentar lo que se recibe de ese proceso 'stdout en un cuadro de texto. Necesito ver cada carácter de salida del proceso secundario de inmediato y no puedo esperar a que se complete una línea, por lo tanto, no creo que el evento Process.OutputDataReceived sea adecuado para mi propósito. ¿Puedes decirme una manera sensata de lograr esto?C# Read stdout of child process asincronically

He intentado llamar al Process.StandardOutput.BaseStream.BeginRead() y pasarle una función de devolución de llamada, pero en esta función de devolución de llamada recibo una excepción de Process.StandardOutput.BaseStream.EndRead().

Mi código es el siguiente (el proceso hijo es un motor de secuencia de comandos - abreviado "SE" -. Para la verificación de la funcionalidad de un dispositivo externo scripts se ejecutan en secuencia y cada script requiere una instancia de la aplicación 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(); 
    } 
} 

lo que pasa es que después de un proceso de sE termina consigo una excepción de tipo InvalidOperationException que dice

StandardOut no ha sido redirigida o el proceso no ha comenzado todavía.

de la llamada a SE_Process.StandardOutput.BaseStream.EndRead(). No entiendo por qué porque he configurado SE_Process.StartInfo.RedirectStandardOutput antes del inicio de cada nuevo proceso. Me parece que si la secuencia estándar de un proceso salido llama a mi función read_SE_StdOut() después de que el proceso haya sido descartado, ¿es posible?

¡Gracias por leer!

+0

Puede que esté interesado en la biblioteca [MedallionShell] (https://github.com/madelson/MedallionShell), lo que simplifica el tratamiento de las secuencias de proceso io, en particular las operaciones de sincronización – ChaseMedallion

Respuesta

2

La excepción que obtienes es normal. Una llamada BeginRead() nunca puede tener éxito: la última, justo después de que finaliza el proceso. Normalmente evitaría volver a llamar al BeginRead() si sabe que el proceso se ha completado para no recibir la excepción. Sin embargo, rara vez lo sabrías. Solo atrapa la excepción. O use BeginOutputReadLine() en su lugar, lo atrapará por usted.

Supongo que también redirige stderr y que la herramienta lo utiliza para dar salida a "X". No hay forma de mantener la salida en stderr y stdout sincronizada después de que se almacena en el búfer y se redirige.

+0

Veo, simplemente manejaré la excepción . Con respecto al orden alterado de los caracteres; también las X: es se ponen en stdout así que no creo que eso explique por qué se presentan fuera de servicio. Dejaré que esta pregunta permanezca sin respuesta porque mi mayor problema es la visualización incorrecta de la salida (pensé que podría haber estado relacionado con la excepción de alguna manera, pero tal vez no lo esté). ¡Gracias por su respuesta! – jokki

+0

Mi problema con la visualización desordenada de los caracteres recibidos ya no ocurre, debo haberlo solucionado por accidente :) He eliminado esa parte de la pregunta anterior y configuré la pregunta como "respondida". ¡Gracias de nuevo! – jokki