J'ai observé que Process.HasExited
renvoie parfois true
même si le processus est toujours en cours d'exécution.
Mon code ci-dessous commence un processus avec le nom "testprogram.exe" puis attend sa fin. Le problème est que parfois je me fais jeter l'exception; il semble que même si HasExited
renvoie true
, le processus lui-même est toujours actif dans le système - comment cela est-il possible ??
Mon programme écrit dans un fichier journal juste avant qu'il ne se termine. Par conséquent, je dois être absolument sûr que ce fichier existe (c'est-à-dire que le processus est terminé/terminé) avant de le lire. Vérifier continuellement son existence n'est pas une option.
// 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");
UPDATE: J'ai juste essayé d'attendre avec process.WaitForExit()
au lieu de la boucle de scrutation et le résultat est exactement le même.
Ajout: Le code ci-dessus était uniquement destiné à démontrer un problème 'plus clair'. Pour le rendre clair; mon problème n'est PAS que je puisse toujours obtenir le processus par Process.GetProcessesByName( "testprogram" );
après qu'il ait défini HasExited
sur true.
Le vrai problème est que le programme que j'exécute en externe écrit un fichier - juste avant - qu'il se termine (gracieusement). J'utilise HasExited
pour vérifier que le processus est terminé et je sais donc que je peux lire le fichier (car le processus s'est terminé!), Mais il semble que HasExited
renvoie true
même lorsque le programme n'a PAS encore écrit le fichier sur le disque. Voici un exemple de code illustrant le problème exact:
// 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");
}
Je me rends compte qu'il s'agit d'un ancien billet, mais dans ma quête pour savoir pourquoi mon application exécutant l'événement Exited avant même son ouverture, j'ai découvert quelque chose qui pourrait être utile aux personnes confrontées à ce problème à l'avenir.
Lorsqu'un processus est démarré, un PID lui est attribué. Si l'utilisateur est ensuite invité à ouvrir la boîte de dialogue Contrôle de compte d'utilisateur et à sélectionner "Oui", le processus est redémarré et un nouveau PID lui est attribué.
Je me suis assis avec ceci pendant quelques heures, j'espère que cela peut sauver du temps à quelqu'un.
Je vous suggère d'essayer de cette façon:
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!");
}
Quoi qu'il en soit ... dans la page MSDN de HasExited, je lis la note éclairée suivante:
Lorsque la sortie standard a été redirigée vers les gestionnaires d'événements asynchrones , Il est possible que le traitement de la sortie ne termine pas le processus Lorsque cette propriété renvoie la valeur true. Pour vous assurer que la gestion des événements asynchrones Est terminée, appelez la surcharge WaitForExit () Qui ne prend aucun paramètre avant de vérifier HasExited.
Cela pourrait être en quelque sorte lié à votre problème car vous redirigez tout.
Donc, juste pour une enquête plus approfondie sur la cause du problème, vous devriez peut-être vérifier ce qui se passe réellement en utilisant Process Monitor . Il suffit de le démarrer, d’inclure le programme externe et votre propre outil, et de le laisser enregistrer ce qui se passe.
Dans le journal, vous devriez voir comment l'outil externe écrit dans le fichier de sortie et comment ouvrir ce fichier. Mais dans ce journal, vous devriez voir dans quel ordre se produisent tous ces accès.
La première chose qui m'est venue à l'esprit est que la classe Process
ne ment pas et que le processus a vraiment disparu quand il le dit. Le problème est qu’à ce stade, il semble que le fichier n’est pas encore totalement disponible. Je pense que ceci est un problème du système d'exploitation, car il contient certaines parties du fichier encore dans un cache qui n'est pas entièrement écrit sur le disque et que l'outil est simplement sorti sans vider ses descripteurs.
En gardant cela à l'esprit, vous devriez voir dans le journal que l'outil externe a créé le fichier, quitté et APRÈS que le fichier sera vidé/fermé (par le système d'exploitation [supprimez éventuellement les filtres lorsque vous avez trouvé ce point dans le journal]).
Donc, si mes hypothèses sont correctes, la cause fondamentale serait le mauvais comportement de votre outil externe que vous ne pouvez pas modifier, ce qui conduit à simplement attendre un peu après la fin du processus et à espérer que le délai est suffisamment long pour que le fichier soit vidé./fermé par le système d'exploitation (essayez peut-être d'ouvrir le fichier en boucle avec un délai d'expiration jusqu'à ce qu'il réussisse).
Je sais, ceci est un ancien post mais peut-être que je peux aider quelqu'un.
La classe Process peut se comporter de manière inattendue! HasExited
retournera true si le processus a quitté ou si le processus s'exécute avec des privilèges d'administrateur et que votre programme ne dispose que de privilèges utilisateur .
Tout d'abord, êtes-vous sûr que testprogram ne génère pas de processus propre et quitte sans attendre la fin du processus? Nous avons affaire à une sorte de situation de concurrence critique, et le programme de test peut être considérable.
Le deuxième point que je voudrais aborder concerne ceci: "Je dois être absolument sûr que ce fichier journal existe". Eh bien, il n'y a pas une telle chose. Vous pouvez faire votre chèque, puis le fichier est parti. La manière habituelle de résoudre ce problème n'est pas de vérifier, mais plutôt de faire ce que vous voulez faire avec le fichier. Allez-y, lisez-le, attrapez les exceptions, réessayez si le problème vous semble instable et que vous ne voulez rien changer. Le check-and-do fonctionnel ne fonctionne pas bien si vous avez plus d'un acteur (thread ou autre) dans le système.
Un tas d'idées aléatoires suit.
Avez-vous essayé d'utiliser FileSystemWatcher et ne pas dépendre de l'achèvement du processus?
Est-ce que ça va mieux si vous essayez de lire le fichier (sans vérifier s'il existe, mais agir à la place) dans l'événement process.Exited? [il ne devrait pas]
Le système est-il en bonne santé? Quelque chose de suspect dans le journal des événements?
Des politiques antivirus vraiment agressives peuvent-elles être impliquées?
(Je ne peux pas en dire beaucoup sans voir tout le code et examiner le programme de test.)
Il y a deux possibilités: l'objet de processus continue à contenir une référence au processus. Il est donc sorti, mais il n'a pas encore été supprimé. Ou vous avez une deuxième instance du processus en cours d'exécution. Vous devez également comparer l'ID de processus pour vous en assurer. Essaye ça.
....
// 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");
Selon Documentation MSDN for HasExited
.
Si un descripteur est ouvert au processus, Le système d'exploitation libère la mémoire de processus À la fin du processus , Mais conserve les informations administratives Relatives au processus, tels que la poignée, le code de sortie et l'heure de sortie.
Probablement pas lié, mais il est à noter.
S'il ne s'agit que d'un problème au 1/10 du temps et que le processus disparaît après une seconde de toute façon, en fonction de votre utilisation de HasExited, essayez simplement d'ajouter un autre délai après que la vérification HasExited fonctionne, comme
while (!process.HasExited)
DoStuff();
Thread.Sleep(500);
Cleanup();
et voir si le problème persiste.
Personnellement, j’ai toujours utilisé le gestionnaire d’événements Exited
à la place de tout type d’interrogation et un wrapper personnalisé simpliste autour de System.Diagnostics.Process
pour gérer des tâches telles que la sécurité des threads, encapsulant un appel à CloseMainWindow()
suivi de WaitForExit(timeout)
et enfin Kill()
, de journalisation, etc. et jamais rencontré un problème.
Pour commencer, existe-t-il un problème d'utilisation de Process.WaitForExit plutôt que de l'interroger?
Quoi qu'il en soit, il est techniquement possible pour le processus de sortir d'un point de vue utilisable, mais le processus est toujours actif pendant qu'il effectue des tâches telles que le vidage du cache du disque. Le fichier journal est-il particulièrement volumineux (ou toute opération effectuée sur des écritures sur disque)?
Peut-être que le problème est dans le programme de test? Ce code est-il bien effacé/fermé, etc.? Il me semble que si programme test écrit un fichier sur le disque, le fichier doit au moins être disponible (vide ou non)
Utilisez process_name.Refresh()
avant de vérifier si le processus s'est terminé ou non. Refresh()
effacera toutes les informations en cache liées au processus.
Si vous avez une application Web et que votre programme/processus externe génère des fichiers (écriture sur disque), vérifiez si vos IIS ont le droit d'écrire dans ce dossier si ce n'est pas sur la sécurité des propriétés, ajoutez une autorisation pour votre IIS utilisateur, c’était la raison dans mon cas, je recevais process.HasExited = true, mais les fichiers produits à partir du processus n’étaient pas terminés, après avoir lutté pendant un certain temps, j’ajoutais des autorisations complètes au dossier dans lequel processus se débattait et processus .Refresh () comme Zarathos a décrit ci-dessus et tout fonctionnait comme prévu.