c# - not - Process.HasExited devuelve true aunque el proceso se está ejecutando?
c# close form (11)
En primer lugar, ¿está seguro de que testprogram no genera un proceso propio y finaliza sin esperar a que finalice ese proceso ? Estamos tratando 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 hacer es sobre esto: "Necesito estar absolutamente seguro de que este archivo de registro existe". Bueno, no hay tal cosa. Usted puede hacer su cheque, y luego el archivo se ha ido. La forma común de abordar esto no es verificar, sino hacer lo que quiere hacer con el archivo. Adelante, léelo, atrapa excepciones, vuelve a intentarlo si la cosa parece inestable y no quieres 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.
Un montón de ideas al azar sigue.
¿Ha intentado usar FileSystemWatcher y no dependiendo de la finalización del proceso?
¿Se pone mejor si intentas leer el archivo (no verificando si existe, sino actuando en su lugar) en el proceso? ¿Evento emitido? [no debería]
¿Está el sistema sano? ¿Algo sospechoso en el registro de eventos?
¿Puede involucrarse alguna política antivirus realmente agresiva?
(No puedo decir mucho sin ver todo el código y sin mirar el programa de prueba).
He estado observando que Process.HasExited
veces devuelve true
aunque el proceso todavía se está ejecutando.
Mi código a continuación inicia un proceso con el nombre "testprogram.exe" y luego espera a que se cierre. El problema es que a veces me tiran 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, debo estar absolutamente seguro de que este archivo de registro existe (también conocido como el proceso ha terminado / finalizado) antes de leerlo. La verificación continua de 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: Acabo de intentar esperar con process.WaitForExit()
lugar del bucle de sondeo y el resultado es el mismo.
Adición: el código anterior solo era para demostrar un problema ''más claro'' por igual. Para hacerlo claro; Mi problema NO es que todavía pueda obtener una retención del proceso por Process.GetProcessesByName( "testprogram" );
después de establecer HasExited
en true.
El problema real es que el programa que estoy ejecutando externamente escribe un archivo, justo antes, termina (con gracia). Utilizo HasExited
para verificar cuándo ha finalizado el proceso y, por lo tanto, sé que puedo leer el archivo (¡porque el proceso terminó!), Pero parece que HasExited
devuelve true
incluso a veces cuando el programa NO ha escrito el archivo en el disco todavía. Aquí hay un código de 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");
}
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 tienes una segunda instancia del proceso en ejecución. También debe comparar la identificación 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");
Lo sé, este es un post viejo, pero tal vez pueda ayudar a alguien.
La clase de proceso puede comportarse inesperadamente! HasExited
devolverá verdadero si el proceso ha finalizado o si el proceso se ejecuta con privilegios de administrador y su programa solo tiene privilegios de usuario .
Me doy cuenta de que esta es una publicación antigua, pero en mi búsqueda para descubrir por qué mi aplicación ejecutó el evento Exited antes de que se abriera, 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 luego se le solicita al usuario el cuadro de diálogo Control de cuenta de usuario y selecciona ''Sí'', el proceso se reinicia y se le asigna un nuevo PID.
Me senté con esto por unas horas, espero que esto pueda ahorrarle tiempo a alguien.
Para empezar, ¿hay algún problema con el uso de Process.WaitForExit en lugar de sondearlo?
De todos modos, es técnicamente posible que el proceso salga desde un punto de vista utilizable, pero el proceso todavía es breve mientras hace cosas como vaciar el caché del disco. ¿Es el archivo de registro especialmente grande (o cualquier operación que esté realizando una gran cantidad de escrituras en el disco)?
Por lo tanto, para una investigación más profunda sobre la causa raíz del problema, tal vez debería revisar lo que realmente está sucediendo mediante el uso de Process Monitor . Simplemente inicie e incluya el programa externo y su propia herramienta y deje que grabe lo que sucede.
Dentro del registro, debe ver cómo la herramienta externa escribe en el archivo de salida y cómo abre ese archivo. Pero dentro de este registro debe ver en qué orden suceden todos estos accesos.
Lo primero que me vino a la mente es que la clase de Process
no miente y el proceso realmente desaparece cuando se lo dice. Entonces, el problema es que en este momento parece que el archivo aún no está completamente disponible. Creo que este es un problema del sistema operativo , ya que mantiene algunas partes del archivo aún dentro de un caché que no está completamente escrito en el disco y la herramienta simplemente se ha cerrado sin vaciar sus manejadores de archivos.
Teniendo esto en cuenta, debería ver dentro del registro que la herramienta externa creó el archivo, salió y DESPUÉS de que el archivo se vaciará / cerrará (el sistema operativo [tal vez elimine los filtros cuando encuentre este punto en el registro]).
Por lo tanto, si mis suposiciones son correctas, la causa raíz sería el mal comportamiento de su herramienta externa que no puede cambiar, por lo tanto, simplemente espera un poco después de que el proceso haya finalizado y espero que el tiempo de espera sea lo suficientemente largo como para que el archivo se vacíe. / cerrado por el sistema operativo (tal vez intente abrir el archivo en un bucle con un tiempo de espera hasta que tenga éxito).
Según la documentación de MSDN para HasExited
.
Si un identificador está abierto para el proceso, el sistema operativo libera la memoria del proceso cuando el proceso ha salido, pero retiene información administrativa sobre el proceso, como el identificador, el código de salida y el tiempo de salida.
Probablemente no esté relacionado, pero vale la pena señalarlo.
Si solo es un problema 1/10 de las veces, y el proceso desaparece después de un segundo de todas formas, dependiendo de su uso de HasExited, intente agregar otra demora después de que la verificación HasExited funcione, como
while (!process.HasExited)
DoStuff();
Thread.Sleep(500);
Cleanup();
y ver si el problema persiste.
Personalmente, siempre he usado el controlador de eventos Exited en lugar de cualquier tipo de sondeo, y un envoltorio personalizado simplista alrededor de System.Diagnostics.Process
para manejar la seguridad de subprocesos, envolver una llamada a CloseMainWindow()
seguido de WaitForExit(timeout)
y finalmente Kill()
, logging, etcétera, y nunca encontré un problema.
Si tiene una aplicación web y su programa / proceso externo está generando archivos (escribir en el disco), compruebe si su IIS tiene derechos para escribir en esa carpeta si no está en el permiso de seguridad de propiedades para su usuario de IIS, esa fue la razón en mi caso , estaba recibiendo process.HasExited = true, pero no se completaron los archivos del proceso, después de luchar durante un tiempo, agregué permisos completos a la carpeta donde el proceso se retorcía y procesaba.Refresh () como describió Zarathos desde arriba y todo estaba trabajando como se esperaba.
Tal vez el problema está en el testprogram? ¿Este código bien ras / cerrar, etc.? Me parece que si testprogram escribe un archivo en el disco, el archivo debería estar al menos disponible (vacío o no)
Te sugiero que lo intentes 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!");
}
De todos modos ... en la página de MSDN de HasExited, estoy leyendo la siguiente nota destacada:
Cuando la salida estándar se ha redirigido a controladores de eventos asíncronos, es posible que el procesamiento de salida no se haya completado cuando esta propiedad devuelva verdadero. Para asegurarse de que se haya completado el manejo asíncrono de eventos, llame a la sobrecarga WaitForExit () que no requiere ningún parámetro antes de verificar HasExited.
Esto podría estar relacionado de alguna manera con su problema, ya que está redirigiendo todo.
Use process_name.Refresh()
antes de verificar si el proceso ha salido o no. Refresh()
borrará toda la información en caché relacionada con el proceso.