Développement d'une application WinForm C # .NET 2.0. Besoin de l'application pour se fermer et se redémarrer.
Application.Restart();
La méthode ci-dessus s'est avérée peu fiable .
Quelle est la meilleure façon de redémarrer l'application?
Développement d'une application WinForm C # .NET 2.0. Besoin de l'application pour se fermer et se redémarrer.
Application.Restart();
La méthode ci-dessus s'est avérée peu fiable .
Quelle est la meilleure façon de redémarrer l'application?
Réponses:
Malheureusement, vous ne pouvez pas utiliser Process.Start () pour démarrer une instance du processus en cours d'exécution. Selon la documentation Process.Start (): "Si le processus est déjà en cours d'exécution, aucune ressource de processus supplémentaire n'est démarrée ..."
Cette technique fonctionnera correctement sous le débogueur VS (car VS fait une sorte de magie qui fait que Process.Start pense que le processus n'est pas déjà en cours d'exécution), mais échouera s'il n'est pas exécuté sous le débogueur. (Notez que cela peut être spécifique au système d'exploitation - il me semble que je me souviens que dans certains de mes tests, cela a fonctionné sur XP ou Vista, mais je me souviens peut-être de l'avoir exécuté sous le débogueur.)
Cette technique est exactement celle utilisée par le dernier programmeur du projet sur lequel je travaille actuellement, et j'essaie de trouver une solution de contournement pour cela depuis un certain temps. Jusqu'à présent, je n'ai trouvé qu'une seule solution, et cela me semble juste sale: démarrer une 2ème application, qui attend en arrière-plan la fin de la première application, puis relance la 1ère application. Je suis sûr que ça marcherait, mais, beurk.
Edit: L'utilisation d'une 2ème application fonctionne. Tout ce que j'ai fait dans la deuxième application était:
static void RestartApp(int pid, string applicationName )
{
// Wait for the process to terminate
Process process = null;
try
{
process = Process.GetProcessById(pid);
process.WaitForExit(1000);
}
catch (ArgumentException ex)
{
// ArgumentException to indicate that the
// process doesn't exist? LAME!!
}
Process.Start(applicationName, "");
}
(Ceci est un exemple très simplifié. Le vrai code a beaucoup de contrôle de cohérence, de gestion des erreurs, etc.)
Process.Startne regarde pas la liste des processus OS en cours d'exécution. Cette déclaration de documentation ne parle que de cette instance d'objet de la Processclasse. La Processclasse peut être attachée à un processus en cours d'exécution, mais elle peut également être dans un état non démarré. À mon avis, c'est une erreur de conception. La meilleure pratique, IMO, est de ne jamais réutiliser une Process instance et de la démarrer immédiatement après sa création. Idéalement, utilisez la Process.Startméthode statique . Ensuite, ce défaut de documentation et de conception n'entre jamais en jeu.
WaitForExit(1000). Mais toute l'attente n'est pas nécessaire pour démarrer un nouveau processus. Il peut s'agir d'un comportement supplémentaire que vous souhaitez, mais il n'est pas nécessaire de démarrer un nouveau processus.
Une approche beaucoup plus simple qui a fonctionné pour moi est:
Application.Restart();
Environment.Exit(0);
Cela préserve les arguments de la ligne de commande et fonctionne malgré les gestionnaires d'événements qui empêcheraient normalement l'application de se fermer.
L'appel Restart () tente de quitter, démarre quand même une nouvelle instance et revient. L'appel Exit () met alors fin au processus sans donner à aucun gestionnaire d'événements une chance de s'exécuter. Il y a une très brève période pendant laquelle les deux processus sont en cours d'exécution, ce qui n'est pas un problème dans mon cas, mais peut-être dans d'autres cas.
Le code de sortie 0 dans Environment.Exit(0);spécifie un arrêt propre. Vous pouvez également quitter avec 1 pour indiquer qu'une erreur s'est produite.
OnClose()événements de formulaire et autres.
Si vous êtes dans le formulaire principal de l'application, essayez d'utiliser
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)fera également le travail.
Enviorment.Exitest une sortie sale et assez invasive car elle empêche l'exécution du code de nettoyage d'application. Pas le bon choix la plupart du temps.
Je suis peut-être en retard à la fête mais voici ma solution simple et cela fonctionne comme un charme avec chaque application que j'ai:
try
{
//run the program again and close this one
Process.Start(Application.StartupPath + "\\blabla.exe");
//or you can use Application.ExecutablePath
//close this one
Process.GetCurrentProcess().Kill();
}
catch
{ }
J'ai eu le même problème exact et j'avais aussi une exigence pour éviter les instances en double - je propose une solution alternative à celle que HiredMind propose (qui fonctionnera bien).
Ce que je fais, c'est démarrer le nouveau processus avec le processId de l'ancien processus (celui qui déclenche le redémarrage) comme argument de ligne cmd:
// Shut down the current app instance.
Application.Exit();
// Restart the app passing "/restart [processId]" as cmd line args
Process.Start(Application.ExecutablePath, "/restart" + Process.GetCurrentProcess().Id);
Ensuite, lorsque la nouvelle application démarre, j'analyse d'abord les arguments de la ligne cm et vérifie si l'indicateur de redémarrage est là avec un processId, puis j'attends que le processus se termine:
if (_isRestart)
{
try
{
// get old process and wait UP TO 5 secs then give up!
Process oldProcess = Process.GetProcessById(_restartProcessId);
oldProcess.WaitForExit(5000);
}
catch (Exception ex)
{
// the process did not exist - probably already closed!
//TODO: --> LOG
}
}
Je ne montre évidemment pas tous les contrôles de sécurité que j'ai en place, etc.
Même si ce n'est pas idéal - je trouve que c'est une alternative valable pour que vous n'ayez pas à avoir en place une application séparée juste pour gérer le redémarrage.
/allowMultipleInstancesdrapeau plutôt qu'un drapeau plutôt étrange /restart.
// Get the parameters/arguments passed to program if any
string arguments = string.Empty;
string[] args = Environment.GetCommandLineArgs();
for (int i = 1; i < args.Length; i++) // args[0] is always exe path/filename
arguments += args[i] + " ";
// Restart current application, with same arguments/parameters
Application.Exit();
System.Diagnostics.Process.Start(Application.ExecutablePath, arguments);
Cela semble mieux fonctionner que Application.Restart ();
Je ne sais pas comment cela gère si votre programme protège contre plusieurs instances. Je suppose que vous feriez mieux de lancer un deuxième .exe qui met en pause puis démarre votre application principale pour vous.
C'est simple, il vous suffit d'appeler les Application.Restart()méthodes qui ont tendance à appeler votre application pour le redémarrage. Mais vous devez avoir à quitter l'environnement local avec leurs codes d'erreur:
Application.Restart();
Environment.exit(int errorcode);
vous pouvez créer une énumération de code d'erreur pour que vous puissiez l'utiliser efficacement.
Une autre méthode consiste simplement à quitter l'application et à démarrer le processus avec le chemin de l'exécutable:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Essayez ce code:
bool appNotRestarted = true;
Ce code doit également être dans la fonction:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
J'ai trouvé une autre solution, peut-être que n'importe qui peut l'utiliser aussi.
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
Le code est simplifié, alors faites attention aux exceptions et tout ça;)
Vous oubliez les options / paramètres de ligne de commande qui ont été transmis à votre instance en cours d'exécution. Si vous ne les transmettez pas, vous n'effectuez pas un véritable redémarrage. Définissez le Process.StartInfoavec un clone des paramètres de votre processus, puis démarrez.
Par exemple, si votre processus a été démarré en tant que myexe -f -nosplash myfile.txt, votre méthode ne s'exécuterait que myexesans tous ces indicateurs et paramètres.
Je voulais que la nouvelle application démarre après la fermeture de l'ancienne.
Utiliser process.WaitForExit () pour attendre l'arrêt de votre propre processus n'a aucun sens. Il expirera toujours.
Donc, mon approche est d'utiliser Application.Exit () puis d'attendre, mais d'autoriser le traitement des événements, pendant un certain temps. Ensuite, démarrez une nouvelle application avec les mêmes arguments que l'ancienne.
static void restartApp() {
string commandLineArgs = getCommandLineArgs();
string exePath = Application.ExecutablePath;
try {
Application.Exit();
wait_allowingEvents( 1000 );
} catch( ArgumentException ex ) {
throw;
}
Process.Start( exePath, commandLineArgs );
}
static string getCommandLineArgs() {
Queue<string> args = new Queue<string>( Environment.GetCommandLineArgs() );
args.Dequeue(); // args[0] is always exe path/filename
return string.Join( " ", args.ToArray() );
}
static void wait_allowingEvents( int durationMS ) {
DateTime start = DateTime.Now;
do {
Application.DoEvents();
} while( start.Subtract( DateTime.Now ).TotalMilliseconds > durationMS );
}
Vous pouvez également utiliser Restarter .
Restarter est une application qui surveille et redémarre automatiquement les programmes et applications bloqués ou bloqués. Il a été développé à l'origine pour surveiller et redémarrer les serveurs de jeux, mais il fera le travail pour n'importe quelle console ou programme ou application basé sur un formulaire.
public static void appReloader()
{
//Start a new instance of the current program
Process.Start(Application.ExecutablePath);
//close the current application process
Process.GetCurrentProcess().Kill();
}
Application.ExecutablePath renvoie le chemin du fichier .exe de votre application Veuillez suivre l'ordre des appels. Vous voudrez peut-être le placer dans une clause try-catch.
Que diriez-vous de créer un fichier de batte, d'exécuter le fichier de commandes avant de fermer, puis de fermer l'instance actuelle.
Le fichier de commandes fait ceci:
Voici mes 2 cents:
La séquence Start New Instance-> Close Current Instance devrait fonctionner même pour les applications qui ne permettent pas d'exécuter plusieurs copies simultanément, car dans ce cas, la nouvelle instance peut recevoir un argument de ligne de commande qui indiquera qu'un redémarrage est en cours il ne sera donc pas nécessaire de vérifier les autres instances en cours d'exécution. En attendant que la première instance se termine, je suis également implémenté s'il est absolument impératif qu'aucune instance ne soit exécutée en parallèle.
Je crains que le redémarrage de l'ensemble de l'application à l'aide de Process n'aborde votre problème de la mauvaise manière.
Un moyen plus simple consiste à modifier le fichier Program.cs pour redémarrer:
static bool restart = true; // A variable that is accessible from program
static int restartCount = 0; // Count the number of restarts
static int maxRestarts = 3; // Maximum restarts before quitting the program
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
while (restart && restartCount < maxRestarts)
{
restart = false; // if you like.. the program can set it to true again
restartCount++; // mark another restart,
// if you want to limit the number of restarts
// this is useful if your program is crashing on
// startup and cannot close normally as it will avoid
// a potential infinite loop
try {
Application.Run(new YourMainForm());
}
catch { // Application has crashed
restart = true;
}
}
}
J'ai eu un problème similaire, mais le mien était lié à une fuite de mémoire ingérable que je ne pouvais pas trouver sur une application qui doit fonctionner 24 heures sur 24, 7 jours sur 7. Avec le client, j'ai convenu que le moment sûr pour redémarrer l'application était 03h00 si la consommation de mémoire dépassait la valeur définie.
J'ai essayé Application.Restart, mais comme il semble utiliser un mécanisme qui démarre une nouvelle instance alors qu'elle est déjà en cours d'exécution, j'ai opté pour un autre schéma. J'ai utilisé l'astuce que le système de fichiers gère jusqu'à ce que le processus qui les a créés meurt. Donc, à partir de l'application, j'ai déposé le fichier sur le disque, et je n'ai pas Dispose()la poignée. J'ai utilisé le fichier pour envoyer l'exécutable «moi-même» et le répertoire de départ également (pour ajouter de la flexibilité).
Code:
_restartInProgress = true;
string dropFilename = Path.Combine(Application.StartupPath, "restart.dat");
StreamWriter sw = new StreamWriter(new FileStream(dropFilename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite));
sw.WriteLine(Application.ExecutablePath);
sw.WriteLine(Application.StartupPath);
sw.Flush();
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Application.StartupPath, "VideoPhill.Restarter.exe"),
WorkingDirectory = Application.StartupPath,
Arguments = string.Format("\"{0}\"", dropFilename)
});
Close();
Close()à la fin, cela déclencherait l'arrêt de l'application et le descripteur de fichier que j'utilisais StreamWriterici serait maintenu ouvert jusqu'à ce que le processus meure vraiment. Ensuite...
Restarter.exe entre en action. Il ESSAYE de lire le fichier en mode exclusif, l'empêchant d'accéder jusqu'à ce que l'application principale ne soit pas morte, puis démarre l'application principale, supprime le fichier et existe. Je suppose que cela ne peut pas être plus simple:
static void Main(string[] args)
{
string filename = args[0];
DateTime start = DateTime.Now;
bool done = false;
while ((DateTime.Now - start).TotalSeconds < 30 && !done)
{
try
{
StreamReader sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
string[] runData = new string[2];
runData[0] = sr.ReadLine();
runData[1] = sr.ReadLine();
Thread.Sleep(1000);
Process.Start(new ProcessStartInfo { FileName = runData[0], WorkingDirectory = runData[1] });
sr.Dispose();
File.Delete(filename);
done = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Thread.Sleep(1000);
}
}
J'utilise ce qui suit et il fait exactement ce que vous recherchez:
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
UpdateCheckInfo info = null;
info = ad.CheckForDetailedUpdate();
if (info.IsUpdateRequired)
{
ad.UpdateAsync(); // I like the update dialog
MessageBox.Show("Application was upgraded and will now restart.");
Environment.Exit(0);
}
Pour utiliser En tant que déconnexion, vous devez mettre fin à toutes les applications de Ram Cache, fermez d'abord l'application, puis réexécutez-la
// en cliquant sur le bouton de déconnexion
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
Le problème de l'utilisation d' Application.Restart () est qu'il démarre un nouveau processus mais que «l'ancien» est toujours en place. C'est pourquoi j'ai décidé de tuer l'ancien processus en utilisant l'extrait de code suivant:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
Et cela fonctionne bien. Dans mon cas, MATLAB et une application C # partagent la même base de données SQLite. Si MATLAB utilise la base de données, le Form-App doit redémarrer (+ Compte à rebours) à nouveau, jusqu'à ce que MATLAB réinitialise son bit occupé dans la base de données. (Juste pour information secondaire)