¿Cómo obtengo la salida de un comando para que aparezca en un control en un Formulario en tiempo real?

De varias fuentes en la web, he reunido el siguiente código para ejecutar un comando a través deCMD.exe y capturando la salida deSTDOUT ySTDERR.

public static class Exec
{
    public delegate void OutputHandler(String line);

    // <summary>
    /// Run a command in a subprocess
    /// </summary>
    /// <param name="path">Directory from which to execute the command</param>
    /// <param name="cmd">Command to execute</param>
    /// <param name="args">Arguments for command</param>
    /// <param name="hndlr">Command output handler (null if none)</param>
    /// <param name="noshow">True if no windows is to be shown</param>
    /// <returns>Exit code from executed command</returns>
    public static int Run(String path, String cmd, String args,
                          OutputHandler hndlr = null, Boolean noshow = true)
    {
        // Assume an error
        int ret = 1;
        // Create a process
        using (var p = new Process())
        {
            // Run command using CMD.EXE
            // (this way we can pipe STDERR to STDOUT so they can get handled together)
            p.StartInfo.FileName = "cmd.exe";
            // Set working directory (if supplied)
            if (!String.IsNullOrWhiteSpace(path)) p.StartInfo.WorkingDirectory = path;
            // Indicate command and arguments
            p.StartInfo.Arguments = "/c \"" + cmd + " " + args + "\" 2>&1";
            // Handle noshow argument
            p.StartInfo.CreateNoWindow = noshow;
            p.StartInfo.UseShellExecute = false;
            // See if handler provided
            if (hndlr != null)
            {
                // Redirect STDOUT and STDERR
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                // Use custom event handler to capture output
                using (var outputWaitHandle = new AutoResetEvent(false))
                {
                    p.OutputDataReceived += (sender, e) =>
                    {
                        // See if there is any data
                        if (e.Data == null)
                        {
                            // Signal output processing complete
                            outputWaitHandle.Set();
                        }
                        else
                        {
                            // Pass string to string handler
                            hndlr(e.Data);
                        }
                    };
                    // Start process
                    p.Start();
                    // Begin async read
                    p.BeginOutputReadLine();
                    // Wait for process to terminate
                    p.WaitForExit();
                    // Wait on output processing complete signal
                    outputWaitHandle.WaitOne();
                }
            }
            else
            {
                // Start process
                p.Start();
                // Wait for process to terminate
                p.WaitForExit();
            }
            // Get exit code
            ret = p.ExitCode;
        }
        // Return result
        return ret;
    }

    // <summary>
    /// Run a command in a subprocess and return output in a variable
    /// </summary>
    /// <param name="path">Directory from which to execute the command</param>
    /// <param name="cmd">Command to execute</param>
    /// <param name="args">Arguments for command</param>
    /// <param name="outp">Variable to contain the output</param>
    /// <returns>Exit code from executed command</returns>
    public static GetOutputReturn GetOutput(String path, String cmd, String args)
    {
        GetOutputReturn ret = new GetOutputReturn();
        ret.ReturnCode = Run(path, cmd, args, (line) =>
                             {
                               ret.Output.AppendLine(line);
                             });
        return ret;
    }
}

public class GetOutputReturn
{
    public StringBuilder Output = new StringBuilder();
    public int ReturnCode = 1;
}

Puedo usar esto en una aplicación de consola de tres maneras diferentes de la siguiente manera:

static void Main(string[] args)
{
    int ret;
    Console.WriteLine("Executing dir with no capture and no window");
    ret = Exec.Run(@"C:\", "dir", "");
    Console.WriteLine("Execute returned " + ret);
    Console.WriteLine("Press enter to continue ...");
    Console.ReadLine();
    Console.WriteLine("Executing dir with no capture and window");
    ret = Exec.Run(@"C:\", "dir", "", null, false);
    Console.WriteLine("Execute returned " + ret);
    Console.WriteLine("Press enter to continue ...");
    Console.ReadLine();
    Console.WriteLine("Executing dir with capture and no window");
    var results = Exec.GetOutput(@"C:\", "dir", "");
    Console.WriteLine(results.Output.ToString());
    Console.WriteLine("Execute returned " + results.ReturnCode);
    Console.ReadLine();
    Console.WriteLine("Executing dir with real-time capture and no window");
    ret = Exec.Run(@"C:\", "dir", "", ShowString);
    Console.WriteLine("Execute returned " + ret);
}

public delegate void StringData(String str);

static void ShowString(String str)
{
    Console.WriteLine(str);
}

public delegate void StringData(String str);

static void ShowString(String str)
{
    Console.WriteLine(str);
}

La primera ejecución no recopila ningún resultado y solo muestra el código de salida.
a segunda ejecución no recopila ningún resultado, pero muestra la ventana.
El efecto de esto es que la salida aparece en la ventana de la consola en tiempo real.
a tercera ejecución usa GetOutput para recopilar la salida.
l efecto de esto es que la salida no aparece hasta que se completa la ejecución.
La última ejecución utiliza un controlador para recibir y mostrar la salida en tiempo real.
En apariencia, parece la segunda ejecución, pero es muy diferente.
Por cada línea de salida que se recibe, se llama ShowString.
Show string simplemente muestra la cadena.
in embargo, podría hacer lo que necesite con los datos.

Estoy tratando de adaptar la última ejecución de modo que pueda actualizar un cuadro de texto con la salida del comando en tiempo real. El problema que estoy teniendo es cómo conseguirlo en el contexto correcto (por falta de un término mejor). Debido a que OutputHandler se llama de forma asíncrona, tiene que usar elInvokeRequired/BeginInvoke/EndInvoke mecanismo para sincronizar con el hilo de la interfaz de usuario. Tengo un pequeño problema con cómo hacer esto con los parámetros. En mi código, el cuadro de texto podría ser uno de varios en un control de pestaña, ya que podrían estar ocurriendo varios "Ejecutar" de fondo.

Hasta ahora tengo esto:

private void btnExecute_Click(object sender, EventArgs e)
{
    // Get currently selected tab page
    var page = tcExecControl.SelectedTab;
    // Get text box (always 3rd control on the page)
    var txt = (TextBox)page.Controls[2];
    // Create string handler
    var prc = new Exec.OutputHandler((String line) =>
                  {
                      if (txt.InvokeRequired)
                          txt.Invoke(new MethodInvoker(() =>
                                     { txt.Text += line; }));
                          else txt.Text += line;
                   });
    // Command and arguments are always 1st and 2nd controls on the page
    var result = Exec.Run(@"C:\", page.Controls[0].Text, page.Controls[1], prc);                              
}

Pero esto no parece estar funcionando. No veo ningún resultado en el txtBox.
e hecho, el programa básicamente se cuelga en el controlado

Si cambio el código para usar GetOutput y luego escribo el resultado resultante en el cuadro de texto, todo funciona. Entonces sé que tengo el comando configurado correctamente. Utilizando el depurador, puedo establecer un punto de interrupción en "if txt.InvokeRequired) "y veo que la primera línea de salida viene correctamente. En este punto, el código toma la ruta verdadera de la instrucción if, pero si establezco un punto de interrupción en latxt.Text += line; línea nunca llega allí.

¿Puede alguien ayudarme? Estoy seguro de que me falta algo.

Respuestas a la pregunta(1)

Su respuesta a la pregunta