Hola. Mi nombre es Johan Hernandez y este es mi blog personal donde expreso mis ideas y opiniones. Si estas en busca de mi blog con temas de Tecnologia e Informatica este se encuentra en http://johansoft.blogspot.com.
Mi foto
Barranquilla, Atlantico, Colombia
Cross-Platform Developer

viernes, julio 4

Ejecutar y Capturar Salida Continua de Proceso con C#

En el namespace System.Diagnostics tenemos las clases Process and ProcessStartInfo. El metodo Process.Start nos permite ejecutar un proceso usando una instancia de ProcessStartInfo como se muestra a continuacion:

   1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe");
   2: Process p = Process.Start(si);

El codigo anterior ejecutara el archivo llamado zip.exe, sin embargo no captura la salida del proceso. Necesitamos realizarle unos ajustes a la instancia de ProcessStartInfo de esta manera:


   1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe");
   2: si.RedirectStandardOutput = true;
   3: si.UseShellExecute = false;
   4: Process p = Process.Start(si);

En la linea 2 y 3 ahora vemos los ajustes, con RedirectStandardOutput con valor true le decimos que necesitamos trabajar o leer el buffer de salida del proceso que estamos ejecutando(zip.exe) y UseShellExecute es requerido para poder establecer la primera propiedad en true ademas de evitar que windows cree una ventana de linea de comandos para mostrarnos la ejecucion del archivo, ahora se ejecutara en modo silencioso y solo puede detenerse usando el metodo Stop de la clase process o con el administrador de tareas de Windows.


Aunque estamos redireccionando el buffer de salida aun no estamos trabajando con el, para poder leerlo usamos la propiedad StandardOutput que es de tipo System.IO.StreamReader. Recordemos que el proceso puede detenerse, demorarse o suspender la escritura en la salida asi que practicamente nuestro programa debe aguardar para leer mas datos de la salida del sub-programa.


Para no detener nuestra aplicacion hasta que se haga una escritura en el sub-programa entonces usaremos Threads. Como esta es una tarea sencilla y no se necesitra control del Thread entonces ThreadPool, ThreadPool contiene un metodo llamado QueueUserWorkItem que recibe un metodo a ejecutar y lo asigna a un Thread que este disponible para ejecutar la operacion. Ejemplo:



   1: ThreadPool.QueueUserWorkItem(delegate
   2:             {
   3:                 char c = char.MinValue;
   4:                 while ((c = (char)p.StandardOutput.Read()) != 0)
   5:                 {
   6:                     if (this.InvokeRequired)
   7:                         this.Invoke(new EventHandler(delegate
   8:                             {
   9:                                 this.textBox1.AppendText(c.ToString());
  10:                             }));
  11:                     else
  12:                     {
  13:                         this.textBox1.AppendText(c.ToString());
  14:                     }
  15:                 }
  16:             });

En el codigo anterior, la linea 4 inicia un bucle para leer 1 caracter de la salida del sub-programa a la vez mientras no sea equivalente a 0, el 0 significa que no hay mas nada que leer.


Como el ejemplo anterior esta realizado sobre un Form, las lineas 6 y 14 actualizan un pequeño TextBox agregandole el nuevo caracter leido de la salida del sub-programa. Cuando se trabajan con Threads y interfaz grafica de Windows(Forms,controles,etc) los cambios en las propiedades de la interfaz grafica(controles, forms) realizados desde otro Thread pueden ser peligrosos y pueden traer resultados inesperados, por eso las lineas 9 y 13 hacen exactanmente lo mismo pero en un contexto diferente, tal vez en otro post explique mejor el asunto.


Finalmente:



   1: ProcessStartInfo si = new ProcessStartInfo(@"zip.exe");
   2: si.RedirectStandardOutput = true;
   3: si.UseShellExecute = false;
   4: Process p = Process.Start(si);
   5: ThreadPool.QueueUserWorkItem(delegate
   6:             {
   7:                 char c = char.MinValue;
   8:                 while ((c = (char)p.StandardOutput.Read()) != 0)
   9:                 {
  10:                     if (this.InvokeRequired)
  11:                         this.Invoke(new EventHandler(delegate
  12:                             {
  13:                                 this.textBox1.AppendText(c.ToString());
  14:                             }));
  15:                     else
  16:                     {
  17:                         this.textBox1.AppendText(c.ToString());
  18:                     }
  19:                 }
  20:             });

prueba

prueba

Prueba de codigo

 

   1:  private void Form1_Load(object sender, EventArgs e)
   2:          {
   3:              ProcessStartInfo si = new ProcessStartInfo(@"C:\Users\Johan Hernandez\Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe");
   4:              si.RedirectStandardOutput = true;
   5:              si.UseShellExecute = false;
   6:              Process p = Process.Start(si);
   7:              ThreadPool.QueueUserWorkItem(delegate
   8:              {
   9:                  char c = char.MinValue;
  10:                  while ((c = (char)p.StandardOutput.Read()) != 0)
  11:                  {
  12:                      if (this.InvokeRequired)
  13:                          this.Invoke(new EventHandler(delegate
  14:                              {
  15:                                  this.textBox1.AppendText(c.ToString());
  16:                              }));
  17:                      else
  18:                      {
  19:                          this.textBox1.AppendText(c.ToString());
  20:                      }
  21:                  }
  22:              });
  23:              
  24:          }


   1: private void Form1_Load(object sender, EventArgs e)
   2:         {
   3:             ProcessStartInfo si = new ProcessStartInfo(@"C:\Users\Johan Hernandez\Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe");
   4:             si.RedirectStandardOutput = true;
   5:             si.UseShellExecute = false;
   6:             Process p = Process.Start(si);
   7:             ThreadPool.QueueUserWorkItem(delegate
   8:             {
   9:                 char c = char.MinValue;
  10:                 while ((c = (char)p.StandardOutput.Read()) != 0)
  11:                 {
  12:                     if (this.InvokeRequired)
  13:                         this.Invoke(new EventHandler(delegate
  14:                             {
  15:                                 this.textBox1.AppendText(c.ToString());
  16:                             }));
  17:                     else
  18:                     {
  19:                         this.textBox1.AppendText(c.ToString());
  20:                     }
  21:                 }
  22:             });
  23:             
  24:         }