Tengo el siguiente código:
info = new System.Diagnostics.ProcessStartInfo("TheProgram.exe", String.Join(" ", args));
info.CreateNoWindow = true;
info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForExit();
Console.WriteLine(p.StandardOutput.ReadToEnd()); //need the StandardOutput contents
Sé que el resultado del proceso que estoy iniciando es de alrededor de 7 MB. Ejecutarlo en la consola de Windows funciona bien. Desafortunadamente programáticamente esto se cuelga indefinidamente en WaitForExit. Tenga en cuenta también que esto no bloquea el código para salidas más pequeñas (como 3 KB).
¿Es posible que el StandardOutput interno en ProcessStartInfo no pueda almacenar 7MB? Si es así, ¿qué debo hacer en su lugar? Si no, ¿qué estoy haciendo mal?
c#
processstartinfo
Epaga
fuente
fuente
Respuestas:
El problema es que si redirige
StandardOutput
y / oStandardError
el búfer interno puede llenarse. Cualquiera sea el orden que use, puede haber un problema:StandardOutput
el proceso puede bloquear el intento de escribir en él, por lo que el proceso nunca termina.StandardOutput
usando ReadToEnd, tu proceso puede bloquearse si el proceso nunca se cierraStandardOutput
(por ejemplo, si nunca termina o si está bloqueado la escrituraStandardError
).La solución es usar lecturas asincrónicas para garantizar que el búfer no se llene. Para evitar los puntos muertos y obtener hasta toda la salida de ambos
StandardOutput
yStandardError
se puede hacer esto:EDITAR: Vea las respuestas a continuación para saber cómo evitar una ObjectDisposedException si se produce el tiempo de espera.
fuente
using
declaraciones de los controladores de eventos tengan que estar por encima de lasusing
declaraciones del proceso en sí?La documentación de
Process.StandardOutput
dice leer antes de esperar, de lo contrario puede llegar a un punto muerto, el fragmento copiado a continuación:fuente
RedirectStandardOutput = true;
y no lo utilizasp.StandardOutput.ReadToEnd();
, obtienes un punto muerto / bloqueo.La respuesta de Mark Byers es excelente, pero solo agregaría lo siguiente:
Los delegados
OutputDataReceived
yErrorDataReceived
deben ser eliminados antesoutputWaitHandle
yerrorWaitHandle
eliminados. Si el proceso continúa enviando datos después de que se haya excedido el tiempo de espera y luego finalice, se accederá a las variablesoutputWaitHandle
yerrorWaitHandle
después de eliminarlas.(Para su información, tuve que agregar esta advertencia como respuesta, ya que no podía comentar sobre su publicación).
fuente
Esta es una solución basada en la Biblioteca de tareas paralelas (TPL) más moderna y esperable para .NET 4.5 y superior.
Ejemplo de uso
Implementación
fuente
El problema con la excepción ObjectDisposedException no controlada se produce cuando se agota el tiempo de espera del proceso. En tal caso, las otras partes de la condición:
No se ejecutan. Resolví este problema de la siguiente manera:
fuente
output
yerror
paraoutputBuilder
? ¿Alguien puede proporcionar una respuesta completa que funcione?Rob respondió y me ahorró algunas horas más de pruebas. Lea el búfer de salida / error antes de esperar:
fuente
WaitForExit()
?ReadToEnd
o métodos similares (comoStandardOutput.BaseStream.CopyTo
) volverán después de leer TODOS los datos. nada va a venir después de élTambién tenemos este problema (o una variante).
Intenta lo siguiente:
1) Agregue un tiempo de espera a p.WaitForExit (nnnn); donde nnnn está en milisegundos.
2) Coloque la llamada ReadToEnd antes de la llamada WaitForExit. Esto es lo que hemos visto recomendar MS.
fuente
Crédito a EM0 para https://stackoverflow.com/a/17600012/4151626
Las otras soluciones (incluidas las de EM0) siguen estancadas para mi aplicación, debido a los tiempos de espera internos y al uso de StandardOutput y StandardError por la aplicación generada. Esto es lo que funcionó para mí:
Editar: se agregó la inicialización de StartInfo a la muestra de código
fuente
Lo resolví de esta manera:
Redirigí la entrada, la salida y el error y manejé la lectura de las secuencias de salida y error. Esta solución funciona para SDK 7- 8.1, tanto para Windows 7 como para Windows 8
fuente
Traté de hacer una clase que resolviera su problema utilizando la lectura de flujo asíncrono, teniendo en cuenta las respuestas de Mark Byers, Rob y SteveBay. Al hacerlo, me di cuenta de que hay un error relacionado con la lectura del flujo de salida del proceso asíncrono.
Informé ese error en Microsoft: https://connect.microsoft.com/VisualStudio/feedback/details/3119134
Resumen:
Probablemente sea mejor usar una lectura asincrónica como la sugerida por otros usuarios para su caso. Pero debe tener en cuenta que podría perder alguna información debido a la condición de la carrera.
fuente
Creo que este es un enfoque simple y mejor (no necesitamos
AutoResetEvent
):fuente
.FileName = Path + @"\ggsci.exe" + @" < obeycommand.txt"
para simplificar tu código también? O tal vez algo equivalente a"echo command | " + Path + @"\ggsci.exe"
si realmente no desea utilizar un archivo obeycommand.txt separado.Ninguna de las respuestas anteriores está haciendo el trabajo.
La solución Rob se cuelga y la solución 'Mark Byers' obtiene la excepción eliminada (probé las "soluciones" de las otras respuestas).
Entonces decidí sugerir otra solución:
Este código se depuró y funciona perfectamente.
fuente
GetProcessOutputWithTimeout
método.Introducción
La respuesta actualmente aceptada no funciona (arroja una excepción) y hay demasiadas soluciones pero no hay código completo. Obviamente, esto está perdiendo el tiempo de muchas personas porque esta es una pregunta popular.
Combinando la respuesta de Mark Byers y la respuesta de Karol Tyl, escribí un código completo basado en cómo quiero usar el método Process.Start.
Uso
Lo he usado para crear un diálogo de progreso alrededor de los comandos de git. Así es como lo he usado:
En teoría, también puedes combinar stdout y stderr, pero no lo he probado.
Código
fuente
Sé que esta es una cena antigua pero, después de leer toda esta página, ninguna de las soluciones funcionaba para mí, aunque no intenté con Muhammad Rehan ya que el código era un poco difícil de seguir, aunque supongo que estaba en el camino correcto. . Cuando digo que no funcionó, eso no es del todo cierto, a veces funcionaría bien, supongo que tiene algo que ver con la longitud de la salida antes de una marca EOF.
De todos modos, la solución que funcionó para mí fue usar diferentes hilos para leer StandardOutput y StandardError y escribir los mensajes.
¡Espero que esto ayude a alguien, que pensó que esto podría ser tan difícil!
fuente
sw.FlushAsync(): Object is not set to an instance of an object. sw is null.
¿Cómo / dónde se debesw
definir?Después de leer todas las publicaciones aquí, me decidí por la solución consolidada de Marko Avlijaš. sin embargo , no resolvió todos mis problemas.
En nuestro entorno tenemos un Servicio de Windows que está programado para ejecutar cientos de archivos .bat .cmd .exe, ... etc. diferentes que se han acumulado a lo largo de los años y fueron escritos por diferentes personas y en diferentes estilos. No tenemos control sobre la escritura de los programas y scripts, solo somos responsables de programar, ejecutar e informar sobre el éxito / el fracaso.
Así que probé casi todas las sugerencias aquí con diferentes niveles de éxito. La respuesta de Marko fue casi perfecta, pero cuando se ejecutó como un servicio, no siempre captó stdout. Nunca llegué al fondo de por qué no.
La única solución que encontramos que funciona en TODOS nuestros casos es esta: http://csharptest.net/319/using-the-processrunner-class/index.html
fuente
Solución que terminé usando para evitar toda la complejidad:
Así que creo un archivo temporal, redirijo tanto el resultado como el error al usarlo
> outputfile > 2>&1
y luego solo leo el archivo una vez que el proceso ha finalizado.Las otras soluciones están bien para escenarios en los que desea hacer otras cosas con la salida, pero para cosas simples esto evita mucha complejidad.
fuente
He leído muchas de las respuestas e hice la mía. No estoy seguro de que este se arregle en cualquier caso, pero se soluciona en mi entorno. Simplemente no estoy usando WaitForExit y uso WaitHandle.WaitAll en las señales de salida y error de finalización. Me alegrará si alguien ve posibles problemas con eso. O si ayudará a alguien. Para mí es mejor porque no usa tiempos de espera.
fuente
Creo que con async, es posible tener una solución más elegante y no tener puntos muertos incluso cuando se usa standardOutput y standardError:
Se basa en la respuesta de Mark Byers. Si no está en un método asíncrono, puede usar en
string output = tStandardOutput.result;
lugar deawait
fuente
Ninguna de esas respuestas me ayudó, pero esta solución funcionó bien con el manejo de bloqueos
https://stackoverflow.com/a/60355879/10522960
fuente
Esta publicación puede estar desactualizada, pero descubrí que la causa principal por la que generalmente se bloquea se debe al desbordamiento de la pila para el redirectStandardoutput o si tiene redirectStandarderror.
Como los datos de salida o los datos de error son grandes, provocará un tiempo de bloqueo ya que todavía se está procesando por una duración indefinida.
para resolver este problema:
fuente
Llamemos al código de muestra publicado aquí el redirector y el otro programa redirigido. Si fuera yo, probablemente escribiría un programa redirigido de prueba que pueda usarse para duplicar el problema.
Así que lo hice. Para los datos de prueba utilicé la especificación de lenguaje ECMA-334 C # v PDF; Es de unos 5 MB. La siguiente es la parte importante de eso.
El valor del tamaño de datos no coincide con el tamaño real del archivo, pero eso no importa. No está claro si un archivo PDF siempre usa CR y LF al final de las líneas, pero eso no importa. Puede usar cualquier otro archivo de texto grande para probar.
Usando eso, el código del redirector de muestra se cuelga cuando escribo la gran cantidad de datos pero no cuando escribo una pequeña cantidad.
Intenté mucho rastrear de alguna manera la ejecución de ese código y no pude. Comenté las líneas del programa redirigido que desactivaron la creación de una consola para que el programa redirigido intentara obtener una ventana de consola separada pero no pude.
Luego encontré Cómo iniciar una aplicación de consola en una nueva ventana, la ventana de los padres o ninguna ventana . Aparentemente, no podemos (fácilmente) tener una consola separada cuando un programa de consola inicia otro programa de consola sin ShellExecute y dado que ShellExecute no admite la redirección, debemos compartir una consola, incluso si no especificamos ninguna ventana para el otro proceso.
Supongo que si el programa redirigido llena un búfer en algún lugar, entonces debe esperar a que se lean los datos y si en ese momento el redirector no lee ningún dato, entonces es un punto muerto.
La solución es no usar ReadToEnd y leer los datos mientras se escriben, pero no es necesario usar lecturas asincrónicas. La solución puede ser bastante simple. Lo siguiente funciona para mí con el PDF de 5 MB.
Otra posibilidad es usar un programa GUI para hacer la redirección. El código anterior funciona en una aplicación WPF excepto con modificaciones obvias.
fuente
Estaba teniendo el mismo problema, pero la razón era diferente. Sin embargo, ocurriría en Windows 8, pero no en Windows 7. La siguiente línea parece haber causado el problema.
La solución fue NO deshabilitar UseShellExecute. Ahora recibí una ventana emergente de Shell, que no es deseada, pero mucho mejor que el programa esperando que no suceda nada en particular. Así que agregué la siguiente solución para eso:
Ahora, lo único que me molesta es por qué esto está sucediendo en Windows 8 en primer lugar.
fuente
UseShellExecute
establecerlo en falso si desea redirigir la salida.