2010-03-25 13 views
27

He estado observando que Process.HasExited a veces devuelve true aunque el proceso todavía se esté ejecutando.Process.HasExited devuelve verdadero aunque el proceso se está ejecutando?

Mi código abajo inicia un proceso con el nombre "testprogram.exe" y luego espera a que salga. El problema es que a veces me arrojan la excepción; parece que aunque HasExited devuelve true, el proceso en sí sigue vivo en el sistema, ¿cómo puede ser esto?

Mi programa escribe en un archivo de registro justo antes de que termine y, por lo tanto, tengo que estar absolutamente seguro de que este archivo de registro existe (es decir, el proceso ha finalizado/terminado) antes de leerlo. Verificar continuamente su existencia no es una opción.

// Create new process object 
process = new Process(); 

// Setup event handlers 
process.EnableRaisingEvents = true; 
process.OutputDataReceived += OutputDataReceivedEvent; 
process.ErrorDataReceived += ErrorDataReceivedEvent; 
process.Exited += ProgramExitedEvent; 

// Setup start info 
ProcessStartInfo psi = new ProcessStartInfo 
          { 
           FileName = ExePath, 
           // Must be false to redirect IO 
           UseShellExecute = false, 
           RedirectStandardOutput = true, 
           RedirectStandardError = true, 
           Arguments = arguments 
          }; 

process.StartInfo = psi; 

// Start the program 
process.Start(); 

while (!process.HasExited) 
    Thread.Sleep(500); 

Process[] p = Process.GetProcessesByName("testprogram"); 

if (p.Length != 0) 
    throw new Exception("Oh oh"); 

ACTUALIZACIÓN: Sólo traté de espera con process.WaitForExit() en lugar del bucle de sondeo y el resultado es exactamente el mismo.

Adición: El código anterior era solo para demostrar un problema "más claro" por igual. Para hacerlo claro; mi problema NO es que todavía pueda obtener el proceso por Process.GetProcessesByName("testprogram"); después de establecer HasExited en verdadero.

El problema real es que el programa que estoy ejecutando externamente escribe un archivo -justo antes- termina (con gracia). Yo uso HasExited para verificar cuando el proceso ha finalizado y sé que puedo leer el archivo (¡porque el proceso salió!), Pero parece que HasExited devuelve true incluso algunas veces cuando el programa aún NO ha escrito el archivo en el disco. Aquí está el código ejemplo que ilustra el problema exacto:

// Start the program 
process.Start(); 

while (!process.HasExited) 
    Thread.Sleep(500); 
// Could also be process.WaitForExit(), makes no difference to the result 

// Now the process has quit, I can read the file it has exported 
if (!File.Exists(xmlFile)) 
{ 
    // But this exception is thrown occasionally, why? 
    throw new Exception("xml file not found"); 
} 
+0

¿Cómo se sale el testprogram.exe? ¿Llamas a Kill() sobre él o lo terminas prematuramente? ¿Saldrá normalmente después de terminar todas sus escrituras? – Tanzelax

+0

No lo sé. Es un programa externo que estoy ejecutando y no tengo el código fuente para él.Todo lo que sé es que está retorciendo un archivo (exportando sus resultados) justo antes de que se cierre porque ese es el comportamiento que he observado a partir de él. El valor de retorno es 0, así que supongo que sale normalmente. Nada ha demostrado lo contrario. – johnrl

+0

@ johnrl ¿Alguna vez resolvió ese problema? ¿Puede compartir su código? – GowthamanSS

Respuesta

1

Para empezar, hay un problema con el uso de Process.WaitForExit en vez de interrogar ella?

De todos modos, es técnicamente posible que el proceso salga desde un punto de vista utilizable, pero el proceso aún se realiza brevemente mientras hace cosas como la memoria caché de memoria. ¿Es el archivo de registro especialmente grande (o cualquier operación que realiza una gran cantidad de grabaciones en el disco)?

+0

El tamaño máximo absoluto de los archivos de registro es de alrededor de 1mb. La razón por la que utilizo el sondeo es que necesito poder abortar la tarea de espera en cualquier momento y con WaitForExit estoy bloqueado al menos hasta el tiempo de espera. En su opinión, es posible que el programa esté descargando sus transmisiones al disco aunque HasExited devuelva verdadero? Me resulta extraño que el proceso no esté completamente vacío/desaparecido en la lista de procesos cuando HasExited es verdadero. No menciona ninguna circunstancia especial en la documentación hasta donde yo sé. – johnrl

+0

Es algo que he visto antes, pero no para tamaños tan pequeños. Como señaló otro comentarista, aunque la clase Process mantendrá una referencia al proceso, es posible que desee llamar a Dispose en la instancia del proceso después de verificar que sale, pero antes de intentar encontrarlo en la lista de procesos. – tyranid

1

Hay dos posibilidades, el objeto de proceso continúa manteniendo una referencia al proceso, por lo que ha salido, pero aún no se ha eliminado. O tiene una segunda instancia del proceso en ejecución. También debe comparar el Id del proceso para asegurarse. Prueba esto.

.... 

    // Start the program 
    process.Start(); 


    while (!process.HasExited) 
     Thread.Sleep(500); 

    Process[] p = Process.GetProcessesByName("testprogram"); 
    if (p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited) 
     throw new Exception("Oh oh"); 
+0

Solo tengo una instancia en ejecución y yo. Intenté mantener abierto el Administrador de tareas de Windows y ver la lista de procesos (también verificando las ID, coinciden), y de hecho cuando mi código arroja la excepción, el proceso todavía está en la lista. A veces se cierra casi al instante cuando se lanza la excepción, otras veces puede tardar uno o dos minutos en desaparecer. No estoy seguro de si se puede confiar en el Administrador de tareas (¿puede demorarlo?), Pero el hecho es que mi código arroja la excepción, no siempre, sino alrededor de la décima parte de las ejecuciones. – johnrl

+0

Una vez que process.HasExited es verdadero, entonces el proceso _hace_ salir. Está muerto. Puede ser un zombie durante un período de tiempo, especialmente si hay controladores abiertos para el objeto de proceso. El hecho de que GetProcessByName() a veces puede devolver ese proceso no significa otra cosa que el proceso sigue siendo un zombie. Debería 'process.Dispose()' para asegurarse de que se libera ese handle. Pero su "prueba" usando GetProcessByName simplemente no es algo razonable de hacer. process.HasExited es la única prueba confiable que puede hacer, y usted ya lo hizo. –

+0

Suena razonable. Pero no entiendo por qué el archivo 'testprogram' escribe (que está justo antes de que se cierre), no se escribe en el disco cuando HasExited es verdadero. ¿Cómo puede ser esto si el proceso es un zombie (entonces debería haber enjuagado todo en el disco, ¿no?). La eliminación del objeto de proceso no tiene ningún efecto en el programa ejecutado por el proceso hasta donde sé, entonces, ¿cómo podría ayudar esto? – johnrl

1

Según MSDN documentation para HasExited.

Si un mango está abierto al proceso, el sistema operativo libera la memoria proceso cuando el proceso ha salido , pero conserva administrativa información sobre el proceso, tal como el mango, el código de salida, y hora de salida

Probablemente no está relacionado, pero vale la pena señalarlo.

Si es sólo un problema décima parte del tiempo, y el proceso desaparece después de un segundo de todos modos, dependiendo de su uso de HasExited, tratan simplemente añadiendo otro retraso después de los trabajos de verificación HasExited, como

while (!process.HasExited) 
    DoStuff(); 
Thread.Sleep(500); 
Cleanup(); 

y ver si el problema persiste.

Personalmente, siempre he acabo de utilizar el controlador de Exited evento en lugar de cualquier tipo de votación, y una envoltura personalizada simplista alrededor System.Diagnostics.Process para manejar cosas como hilo de seguridad, envolviendo una llamada a CloseMainWindow() seguido por WaitForExit(timeout)Kill() y finalmente, la tala , etcétera, y nunca se encontró un problema.

1

¿Tal vez el problema está en el programa de prueba? ¿Este código funciona correctamente? Me parece que si testprogram escribe un archivo en el disco, el archivo debe al menos estar disponible (vacía o no)

6

Yo sugeriría a probar de esta manera:

process.Start(); 

while (!process.HasExited) 
{ 
    // Discard cached information about the process. 
    process.Refresh(); 

    // Just a little check! 
    Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString()); 

    Thread.Sleep(500); 
} 

foreach (Process current in Process.GetProcessesByName("testprogram")) 
{ 
    if ((current.Id == process.Id) && !current.HasExited) 
     throw new Exception("Oh oh!"); 
} 

En fin ... en MSDN página de HasExited estoy leyendo la siguiente nota hightlighted:

cuando la salida estándar se ha redirigido a eventos asíncronos manipuladores, es posible que el procesamiento de salida no se habrá completado cuando esta propiedad devuelve cierto. Para asegurarse de que se haya completado el manejo de eventos asíncronos , llame a la sobrecarga de WaitForExit() que no toma ningún parámetro antes de verificar HasExited.

Eso podría estar relacionado de alguna manera con su problema ya que está redireccionando todo.

3

Por lo tanto, para una investigación más a fondo sobre la causa raíz del problema, tal vez debería verificar lo que realmente está sucediendo al usar Process Monitor. Simplemente inícielo e incluya el programa externo y su propia herramienta y deje que registre lo que sucede.

Dentro del registro, debería ver cómo la herramienta externa escribe en el archivo de salida y cómo se abre ese archivo. Pero dentro de este registro debe ver en qué orden ocurren todos estos accesos.

Lo primero que me vino a la mente es que la clase Process no miente y el proceso realmente se va cuando lo dice. Entonces, el problema es que en este momento parece que el archivo aún no está completamente disponible. Creo que se trata de un problema del sistema operativo, porque mantiene algunas partes del archivo todavía dentro de un caché que no está completamente escrito en el disco y la herramienta simplemente se ha salido sin haber descargado sus identificadores de archivo.

Con esto en mente, debería ver dentro del registro que la herramienta externa creó el archivo, salió y DESPUÉS de que el archivo se vaciará/cerró (por el sistema operativo [quizás elimine cualquier filtro cuando encuentre este punto dentro del registro ]).

Así que si mis suposiciones son correctas la causa sería el mal comportamiento de su herramienta externa que no se puede cambiar lo que conduce a simplemente esperar un poco después de que el proceso ha salido y la esperanza de que el tiempo de espera es el tiempo suficiente para obtener el archivo descargado/cerrado por el sistema operativo (tal vez intente abrir el archivo en un bucle con un tiempo de espera hasta que tenga éxito).

4

En primer lugar, ¿está seguro de que testprogram no genera un proceso propio y sale sin esperar a que el proceso termine? Estamos lidiando con algún tipo de condición de carrera aquí, y el programa de prueba puede ser significativo.

El segundo punto que me gustaría comentar es sobre esto: "Tengo que estar absolutamente seguro de que este archivo de registro existe". Bueno, no hay tal cosa. Puede hacer su verificación, y luego el archivo se va. La forma más común de abordar esto no es verificar, sino hacer lo que quiera hacer con el archivo. Adelante, léelo, capte excepciones, vuelva a intentar si la cosa parece inestable y no quiere cambiar nada. El check-and-do funcional no funciona bien si tiene más de un actor (hilo o lo que sea) en el sistema.

Siguen varias ideas aleatorias.

¿Ha intentado utilizar FileSystemWatcher y no depende de la finalización del proceso?

¿Es mejor si intenta leer el archivo (no compruebas si existe, pero en su lugar actuando) en el proceso. ¿Evento salido? [no debería]

¿El sistema está en buen estado? ¿Algo sospechoso en el registro de eventos?

¿Se puede involucrar una política de antivirus realmente agresiva?

(No se puede decir mucho sin ver todo el código y mirando en testprogram.)

0

Si tiene aplicación web, y su programa/proceso externo es generar archivos (escribir en el disco) comprobar si tienen sus IIS derechos para escribir en esa carpeta si no está en la seguridad de las propiedades agregar permiso para su usuario de IIS, esa fue la razón en mi caso, estaba recibiendo process.HasExited = true, pero los archivos producidos del proceso no se completaron, después de luchar por un tiempo agregué permisos completos a la carpeta donde el proceso se retorcía y process.Refresh() como Zarathos describió desde arriba y todo estaba funcionando como se esperaba.

10

Me doy cuenta de que esta es una publicación anterior, pero en mi búsqueda para descubrir por qué mi aplicación ejecutando el evento Exited antes de la aplicación se había abierto descubrí algo que pensé que podría ser útil para las personas que experimentan este problema en el futuro .

Cuando se inicia un proceso, se le asigna un PID. Si al usuario se le solicita el diálogo de Control de cuentas de usuario y selecciona 'Sí', el proceso se reinicia y se le asigna un nuevo PID.

Me senté con esto durante unas horas, con suerte esto puede ahorrarle tiempo a alguien.

4

Lo sé, esta es una publicación anterior, pero puedo ayudar a alguien.
¡La clase de proceso miente a veces! HasExited devolverá verdadero si el proceso ha salido de o si el proceso se ejecuta con privilegios de administrador y su programa solo tiene privilegios de usuario.

-1

Utilice process_name.Refresh() antes de comprobar si el proceso ha finalizado o no. Refresh() borrará toda la información en caché relacionada con el proceso.

+0

Veo que esto fue votado negativamente - ¿Alguna idea de por qué? Tengo un problema similar al de OP y solo trato de ordenar todas estas respuestas ... –

Cuestiones relacionadas