2012-01-10 20 views
17

Tengo un problema en mi proyecto. Me gustaría lanzar un proceso, 7z.exe (versión de consola). que he probado tres cosas diferentes:Obtener salida en vivo del proceso

  • Process.StandardOutput.ReadToEnd();
  • OutputDataReceived & BeginOutputReadLine
  • StreamWriter funciona

Nada. Siempre "espera" que el final del proceso muestre lo que quiero. No tengo ningún código para poner, solo si quiere mi código con una de las cosas enumeradas arriba. Gracias.

Editar: Mi código:

 process.StartInfo.UseShellExecute = false; 
     process.StartInfo.RedirectStandardOutput = true; 
     process.StartInfo.CreateNoWindow = true; 
     process.Start(); 

     this.sr = process.StandardOutput; 
     while (!sr.EndOfStream) 
     { 
      String s = sr.ReadLine(); 
      if (s != "") 
      { 
       System.Console.WriteLine(DateTime.Now + " - " + s); 
      } 
     } 

O

process.StartInfo.UseShellExecute = false; 
process.StartInfo.RedirectStandardOutput = true; 
process.OutputDataReceived += new DataReceivedEventHandler(recieve); 
process.StartInfo.CreateNoWindow = true; 
process.Start(); 
process.BeginOutputReadLine(); 
process.WaitForExit(); 
public void recieve(object e, DataReceivedEventArgs outLine) 
{ 
    System.Console.WriteLine(DateTime.Now + " - " + outLine.Data); 
} 

O

process.StartInfo.UseShellExecute = false; 
process.StartInfo.RedirectStandardOutput = true; 
process.Start(); 
string output = p.StandardOutput.ReadToEnd(); 
process.WaitForExit(); 

Donde "proceso" es mi proceso de pre-hechos

Ok Yo sé Por qué eso no funciona correctamente: 7z.exe es el error: muestra una carga porcentual en la consola y envía información solo cuando el archivo actual ha finalizado. En la extracción, por ejemplo, funciona bien :). Buscaré otra forma de usar funciones 7z sin 7z.exe (tal vez con 7za.exe o con alguna DLL). Gracias a todos. Para responder a la pregunta, ¡el evento OuputDataRecieved funciona bien!

+0

razón por la que no utiliza el DLL/SDK downloadabe de 7zip que permite un control mucho mayor que cualquier técnica basada en la consola? – Yahia

+0

Ayudaría a ver el código que ha probado con Process por ejemplo, donde está creando el Proceso – MethodMan

+0

Porque 7z.exe cubre todas las funciones que quería. – Extaze

Respuesta

16

echar un vistazo a esta página, se ve esta es la solución para usted : http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx y http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx

[Editar] Este es un ejemplo de trabajo:

 Process p = new Process(); 
     p.StartInfo.RedirectStandardError = true; 
     p.StartInfo.RedirectStandardOutput = true; 
     p.StartInfo.UseShellExecute = false; 
     p.StartInfo.CreateNoWindow = true; 
     p.StartInfo.FileName = @"C:\Program Files (x86)\gnuwin32\bin\ls.exe"; 
     p.StartInfo.Arguments = "-R C:\\"; 

     p.OutputDataReceived += new DataReceivedEventHandler(
      (s, e) => 
      { 
       Console.WriteLine(e.Data); 
      } 
     ); 
     p.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine(e.Data); }); 

     p.Start(); 
     p.BeginOutputReadLine(); 

Btw, ls -R C: \ muestra todos los archivos desde la raíz de C: recursivamente. Estos son muchos archivos, y estoy seguro de que no se hará cuando aparezcan los primeros resultados en la pantalla. Existe la posibilidad de que 7zip contenga la salida antes de mostrarla. No estoy seguro de qué parámetros le das al proceso.

+0

Mira mi código:/ – Extaze

+0

Todo lo que puedo encontrar es que deberías usar el evento OutputDataReceived, por el momento no tengo tiempo para probarlo, tal vez más tarde. (http://harbertc.wordpress.com/2006/05/16/reading-text-from-a-process-executed-programmatically-in-c/) –

+0

Intenté todo, pero el problema era 7z.exe. Decidí tomar una biblioteca 7z para C# (SevenZipSharp) y el problema fue resuelto. Gracias;) – Extaze

0

Pruebe esto.

 Process notePad = new Process(); 

     notePad.StartInfo.FileName = "7z.exe"; 
     notePad.StartInfo.RedirectStandardOutput = true; 
     notePad.StartInfo.UseShellExecute = false; 

     notePad.Start(); 
     StreamReader s = notePad.StandardOutput; 



     String output= s.ReadToEnd(); 


     notePad.WaitForExit(); 

Vamos a lo anterior sea en un thread.

Ahora para la actualización de la salida de la interfaz de usuario, se puede utilizar un timer con dos líneas

Console.Clear(); 
    Console.WriteLine(output); 

Esto puede ayudarle a

+1

Como fas como puedo ver, este código también esperará hasta que esté hecho. Como se indicó en la pregunta, él no quiere esperar hasta que esté listo, pero quiere una salida en vivo –

+0

Como dijo Michiel, eso es para obtener la salida "final" – Extaze

1

He utilizado la clase CmdProcessor descrita here en varios proyectos con mucho éxito. Parece un poco desalentador al principio, pero es muy fácil de usar.

+0

No funciona ... Espera a que el proceso se detenga para mostrarme información ... – Extaze

4

No sé si alguien todavía está buscando una solución a esto, pero me ha surgido varias veces porque estoy escribiendo una herramienta en Unity para algunos juegos y debido a la limitada interoperabilidad de ciertos sistemas con mono (como PIA para leer texto de Word, por ejemplo), a menudo tengo que escribir ejecutables específicos del sistema operativo (a veces Windows, a veces MacOS) y ejecutarlos desde Process.Start().

El problema es que cuando se ejecuta un ejecutable como este, se activará en otro subproceso que bloquea la aplicación principal, causando un bloqueo. Si desea proporcionar comentarios útiles a sus usuarios durante este tiempo más allá de los íconos giratorios evocados por su sistema operativo respectivo, entonces está medio enredado. El uso de una transmisión no funcionará porque el hilo aún está bloqueado hasta que finalice la ejecución.

La solución que he encontrado, que puede parecer extrema para algunas personas pero me parece que funciona bastante bien, es usar sockets y multiprocesamiento para configurar comunicaciones síncronas confiables entre las dos aplicaciones. Por supuesto, esto solo funciona si estás creando ambas aplicaciones. Si no, creo que no tienes suerte. ... Me gustaría ver si funciona solo con multihebra usando un enfoque de transmisión tradicional, por lo que si alguien quisiera probar eso y publicar los resultados aquí, sería genial.

De todos modos, aquí está la solución que actualmente trabaja para mí:

En la aplicación principal, o llamando, hago algo como esto:

/// <summary> 
/// Handles the OK button click. 
/// </summary> 
private void HandleOKButtonClick() { 
string executableFolder = ""; 

#if UNITY_EDITOR 
executableFolder = Path.Combine(Application.dataPath, "../../../../build/Include/Executables"); 
#else 
executableFolder = Path.Combine(Application.dataPath, "Include/Executables"); 
#endif 

EstablishSocketServer(); 

var proc = new Process { 
    StartInfo = new ProcessStartInfo { 
     FileName = Path.Combine(executableFolder, "WordConverter.exe"), 
     Arguments = locationField.value + " " + _ipAddress.ToString() + " " + SOCKET_PORT.ToString(), 
     UseShellExecute = false, 
     RedirectStandardOutput = true, 
     CreateNoWindow = true 
    } 
}; 

proc.Start(); 

es donde yo establezca el servidor de socket aquí:

/// <summary> 
/// Establishes a socket server for communication with each chapter build script so we can get progress updates. 
/// </summary> 
private void EstablishSocketServer() { 
    //_dialog.SetMessage("Establishing socket connection for updates. \n"); 
    TearDownSocketServer(); 

    Thread currentThread; 

    _ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; 
    _listener = new TcpListener(_ipAddress, SOCKET_PORT); 
    _listener.Start(); 

    UnityEngine.Debug.Log("Server mounted, listening to port " + SOCKET_PORT); 

    _builderCommThreads = new List<Thread>(); 

    for (int i = 0; i < 1; i++) { 
     currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage)); 
     _builderCommThreads.Add(currentThread); 
     currentThread.Start(); 
    } 
} 

/// <summary> 
/// Tears down socket server. 
/// </summary> 
private void TearDownSocketServer() { 
    _builderCommThreads = null; 

    _ipAddress = null; 
    _listener = null; 
} 

Aquí está mi controlador de socket para el hilo ... tenga en cuenta que tendrá que crear varios hilos en algunos casos; es por eso que tengo que _builderCommThreads Lista allí (I portado desde el código en otro lugar donde yo estaba haciendo algo similar, pero llamando varias instancias en una fila):

/// <summary> 
/// Handles the incoming socket message. 
/// </summary> 
private void HandleIncomingSocketMessage() { 
    if (_listener == null) return; 

    while (true) { 
     Socket soc = _listener.AcceptSocket(); 
     //soc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000); 
     NetworkStream s = null; 
     StreamReader sr = null; 
     StreamWriter sw = null; 
     bool reading = true; 

     if (soc == null) break; 

     UnityEngine.Debug.Log("Connected: " + soc.RemoteEndPoint); 

     try { 
      s = new NetworkStream(soc); 
      sr = new StreamReader(s, Encoding.Unicode); 
      sw = new StreamWriter(s, Encoding.Unicode); 
      sw.AutoFlush = true; // enable automatic flushing 

      while (reading == true) { 
       string line = sr.ReadLine(); 

       if (line != null) { 
        //UnityEngine.Debug.Log("SOCKET MESSAGE: " + line); 
        UnityEngine.Debug.Log(line); 

        lock (_threadLock) { 
         // Do stuff with your messages here 
        } 
       } 
      } 

      // 
     } catch (Exception e) { 
      if (s != null) s.Close(); 
      if (soc != null) soc.Close(); 
      UnityEngine.Debug.Log(e.Message); 
      //return; 
     } finally { 

     // 
     if (s != null) s.Close(); 
     if (soc != null) soc.Close(); 

     UnityEngine.Debug.Log("Disconnected: " + soc.RemoteEndPoint); 
     } 
    } 

    return; 
} 

Por supuesto, tendrá que declarar algunas cosas hasta en la parte superior:

private TcpListener _listener = null; 
private IPAddress _ipAddress = null; 
private List<Thread> _builderCommThreads = null; 
private System.Object _threadLock = new System.Object(); 

... entonces en el ejecutable invocado, configurar el otro extremo (utilicé la estática en este caso, se puede usar cualquier cosa que quiera):

private static TcpClient _client = null; 
private static Stream _s = null; 
private static StreamReader _sr = null; 
private static StreamWriter _sw = null; 
private static string _ipAddress = ""; 
private static int _port = 0; 
private static System.Object _threadLock = new System.Object(); 

/// <summary> 
/// Main method. 
/// </summary> 
/// <param name="args"></param> 
static void Main(string[] args) { 
    try { 
     if (args.Length == 3) { 
      _ipAddress = args[1]; 
      _port = Convert.ToInt32(args[2]); 

      EstablishSocketClient(); 
     } 

     // Do stuff here 

     if (args.Length == 3) Cleanup(); 
    } catch (Exception exception) { 
     // Handle stuff here 
     if (args.Length == 3) Cleanup(); 
    } 
} 

/// <summary> 
/// Establishes the socket client. 
/// </summary> 
private static void EstablishSocketClient() { 
    _client = new TcpClient(_ipAddress, _port); 

    try { 
     _s = _client.GetStream(); 
     _sr = new StreamReader(_s, Encoding.Unicode); 
     _sw = new StreamWriter(_s, Encoding.Unicode); 
     _sw.AutoFlush = true; 
    } catch (Exception e) { 
     Cleanup(); 
    } 
} 

/// <summary> 
/// Clean up this instance. 
/// </summary> 
private static void Cleanup() { 
    _s.Close(); 
    _client.Close(); 

    _client = null; 
    _s = null; 
    _sr = null; 
    _sw = null; 
} 

/// <summary> 
/// Logs a message for output. 
/// </summary> 
/// <param name="message"></param> 
private static void Log(string message) { 
    if (_sw != null) { 
     _sw.WriteLine(message); 
    } else { 
     Console.Out.WriteLine(message); 
    } 
} 

. ..YO' Estoy usando esto para lanzar una herramienta de línea de comando en Windows que usa el material de PIA para sacar texto de un documento de Word. Probé PIA los .dlls en Unity, pero encontré problemas de interoperabilidad con mono. También lo estoy usando en MacOS para invocar scripts de shell que inician instancias adicionales de Unity en modo batch y ejecutar scripts de editor en aquellas instancias que responden a la herramienta a través de esta conexión de socket. Es genial, porque ahora puedo enviar comentarios al usuario, depurar, monitorear y responder a pasos específicos en el proceso, etcétera, etcétera.

HTH

4

para controlar correctamente la redirección de salida y/o error también es necesario redireccionar entrada. Parece ser una característica/error en el tiempo de ejecución de la aplicación externa que está comenzando y de lo que he visto hasta ahora, no se menciona en ningún otro lado.

Ejemplo de uso:

 Process p = new Process(...); 

     p.StartInfo.UseShellExecute = false; 
     p.StartInfo.RedirectStandardOutput = true; 
     p.StartInfo.RedirectStandardError = true; 
     p.StartInfo.RedirectStandardInput = true; // Is a MUST! 
     p.EnableRaisingEvents = true; 

     p.OutputDataReceived += OutputDataReceived; 
     p.ErrorDataReceived += ErrorDataReceived; 

     Process.Start(); 

     p.BeginOutputReadLine(); 
     p.BeginErrorReadLine(); 

     p.WaitForExit(); 

     p.OutputDataReceived -= OutputDataReceived; 
     p.ErrorDataReceived -= ErrorDataReceived; 

...

void OutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     // Process line provided in e.Data 
    } 

    void ErrorDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     // Process line provided in e.Data 
    } 
+1

El redireccionamiento de la entrada estándar me solucionó el problema. –

Cuestiones relacionadas