2010-12-15 18 views
6

Tengo problemas para redirigir la salida de la consola a un cuadro de texto de formularios de Windows. El problema está relacionado con el hilo. Estoy corriendo una aplicación de consola de la siguiente manera,¿Cómo redirecciono la salida de un programa de consola a un cuadro de texto de una manera segura?

private void RunConsoleApp() 
{ 
    Process proc = new Process(); 
    proc.StartInfo.FileName = "app.exe"; 
    proc.StartInfo.Arguments = "-a -b -c"; 
    proc.StartInfo.UseShellExecute = false; 

    // set up output redirection 
    proc.StartInfo.RedirectStandardOutput = true; 
    proc.StartInfo.RedirectStandardError = true;  
    proc.EnableRaisingEvents = true; 
    proc.StartInfo.CreateNoWindow = true; 

    // Set the data received handlers 
    proc.ErrorDataReceived += proc_DataReceived; 
    proc.OutputDataReceived += proc_DataReceived; 

    proc.Start(); 
    proc.BeginErrorReadLine(); 
    proc.BeginOutputReadLine(); 
    proc.WaitForExit(); 

    if (proc.ExitCode == 0) 
    { 
     out_txtbx.AppendText("Success." + Environment.NewLine); 
    } 
    else 
    { 
     out_txtbx.AppendText("Failed." + Environment.NewLine); 
    } 
} 

y luego capturar y procesar los datos con este gestor de salida,

// Handle the date received by the console process 
void proc_DataReceived(object sender, DataReceivedEventArgs e) 
{ 
    if (e.Data != null) 
    { 
     if ((e.Data.EndsWith("DONE.")) || (e.Data.EndsWith("FAILED.")) || 
      (e.Data.StartsWith("RESET"))) 
     { 
      // This crashes the application, but is supposedly the correct method 
      this.AppendText(e.Data + Environment.NewLine); 

      // This works, but the debugger keeps warning me that the call 
      // is not thread safe 
      //out_txtbx.AppendText(e.Data + Environment.NewLine); 
     } 
    } 
} 

El texto de la consola se añade luego como este,

delegate void AppendTextDelegate(string text); 

// Thread-safe method of appending text to the console box 
private void AppendText(string text) 
{ 
    // Use a delegate if called from a different thread, 
    // else just append the text directly 
    if (this.out_txtbx.InvokeRequired) 
    { 
     // Application crashes when this line is executed 
     out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text }); 
    } 
    else 
    { 
     this.out_txtbx.AppendText(text); 
    } 
} 

De toda la documentación y ejemplos que he visto, este parece ser el método correcto, excepto que está bloqueando la aplicación cuando se llama out_txtbx.Invoke.

¿Qué podría romperse y qué formas alternativas hay para hacer esto?


Solución (como señala Hans Passant)

El problema es que la aplicación se ha quedado atascado en un "abrazo mortal" como resultado de la línea,

proc.WaitForExit(); 

Esa línea debe eliminarse y el método debe verse así,

private void RunConsoleApp() 
{ 
    Process proc = new Process(); 
    proc.StartInfo.FileName = "app.exe"; 
    proc.StartInfo.Arguments = "-a -b -c"; 
    proc.StartInfo.UseShellExecute = false; 

    // set up output redirection 
    proc.StartInfo.RedirectStandardOutput = true; 
    proc.StartInfo.RedirectStandardError = true;  
    proc.EnableRaisingEvents = true; 
    proc.StartInfo.CreateNoWindow = true; 

    // Set the data received handlers 
    proc.ErrorDataReceived += proc_DataReceived; 
    proc.OutputDataReceived += proc_DataReceived; 

    // Configure the process exited event 
    proc.Exited += new EventHandler(ProcExited); 

    proc.Start(); 
    proc.BeginErrorReadLine(); 
    proc.BeginOutputReadLine(); 

    // This blocks the main thread and results in "deadly embrace" 
    // The Process.Exited event should be used to avoid this. 
    //proc.WaitForExit(); 
} 

y un controlador de eventos debe ser proporcionada,

/// <summary> 
/// Actions to take when console process completes 
/// </summary> 
private void ProcExited(object sender, System.EventArgs e) 
{ 
    Process proc = (Process)sender; 

    // Wait a short while to allow all console output to be processed and appended 
    // before appending the success/fail message. 
    Thread.Sleep(40); 

    if (proc.ExitCode == 0) 
    { 
     this.AppendText("Success." + Environment.NewLine); 
     ExitBootloader(); 
    } 
    else 
    { 
     this.AppendText("Failed." + Environment.NewLine); 
    } 

    proc.Close(); 
} 
+2

¿Qué error obtienes? – SLaks

+0

¿Y en qué línea? – decyclone

+0

Lo he hecho sin error. Si no hay una respuesta adecuada para cuando pueda acceder a mi muestra de código, la publicaré. Tenga en cuenta que esto será más tarde esta noche cuando llegue a casa. Creé un objeto TextWriter que escribió en un cuadro de texto, luego dirigí Console.SetOut al TextWriter. Consulte http://msdn.microsoft.com/en-us/library/system.console.setout%28v=VS.90%29.aspx – IAbstract

Respuesta

9
proc.WaitForExit(); 

Se llama punto muerto. Su hilo principal está bloqueado, esperando que el proceso salga. Eso le impide ocuparse de los deberes esenciales. Me gusta mantener la UI actualizada. Y asegurándose de que se envíen las solicitudes Control.Invoke(). Eso evita que el método AppendText() se complete. Lo cual detiene el proceso de salida. Lo que evita que su subproceso de interfaz de usuario supere la llamada WaitForExit(). "Abrazo mortal", también conocido como punto muerto.

No puede bloquear el hilo principal. Use el evento Process.Exited en su lugar.

+0

Gracias Hans, tiene mucho sentido (ahora que lo has explicado). –

0

tratar

out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), text); 
+1

Esto no funciona –

Cuestiones relacionadas