Sviluppo di un'applicazione WinForm C # .NET 2.0. È necessario che l'applicazione si chiuda e si riavvii.
Application.Restart();
Il metodo sopra si è dimostrato inaffidabile .
Qual è un modo migliore per riavviare l'applicazione?
Sviluppo di un'applicazione WinForm C # .NET 2.0. È necessario che l'applicazione si chiuda e si riavvii.
Application.Restart();
Il metodo sopra si è dimostrato inaffidabile .
Qual è un modo migliore per riavviare l'applicazione?
Risposte:
Sfortunatamente non puoi usare Process.Start () per avviare un'istanza del processo attualmente in esecuzione. Secondo la documentazione di Process.Start (): "Se il processo è già in esecuzione, non viene avviata alcuna risorsa di processo aggiuntiva ..."
Questa tecnica funzionerà bene con il debugger VS (perché VS fa una sorta di magia che fa sì che Process.Start pensi che il processo non sia già in esecuzione), ma fallirà se non viene eseguito nel debugger. (Nota che questo potrebbe essere specifico del sistema operativo: mi sembra di ricordare che in alcuni dei miei test ha funzionato su XP o Vista, ma potrei semplicemente ricordarmi di eseguirlo sotto il debugger.)
Questa tecnica è esattamente quella usata dall'ultimo programmatore del progetto su cui sto attualmente lavorando, e ho cercato di trovare una soluzione per questo per un po 'di tempo. Finora, ho trovato solo una soluzione, e mi sembra solo sporca e disordinata: avvia una seconda applicazione, che attende in background la fine della prima applicazione, quindi riavvia la prima applicazione. Sono sicuro che funzionerebbe, ma, bleah.
Modifica: funziona una seconda applicazione. Tutto quello che ho fatto nella seconda app è stato:
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, "");
}
(Questo è un esempio molto semplificato. Il codice reale ha molti controlli di integrità, gestione degli errori, ecc.)
Process.Startnon guarda l'elenco dei processi del sistema operativo in esecuzione. Questa dichiarazione di documentazione parla solo di quell'istanza dell'oggetto della Processclasse. La Processclasse può essere collegata a un processo in esecuzione, ma può anche trovarsi in uno stato non avviato. Secondo me questo è un errore di progettazione. La migliore pratica, IMO, è di non riutilizzare mai Process un'istanza e di avviarla immediatamente dopo la creazione. Idealmente, usa il Process.Startmetodo statico . Quindi, questa documentazione e questo difetto di progettazione non entrano mai in gioco.
WaitForExit(1000). Ma l'intera attesa non è necessaria per iniziare un nuovo processo. Potrebbe essere un comportamento aggiuntivo che desideri, ma non è necessario avviare un nuovo processo.
Un approccio molto più semplice che ha funzionato per me è:
Application.Restart();
Environment.Exit(0);
Ciò preserva gli argomenti della riga di comando e funziona nonostante i gestori di eventi che normalmente impedirebbero la chiusura dell'applicazione.
La chiamata Restart () tenta di uscire, avvia comunque una nuova istanza e ritorna. La chiamata Exit () termina quindi il processo senza dare la possibilità a nessun gestore di eventi di essere eseguito. C'è un periodo molto breve in cui entrambi i processi sono in esecuzione, il che non è un problema nel mio caso, ma forse in altri casi.
Il codice di uscita 0 in Environment.Exit(0);specifica un arresto pulito. È inoltre possibile uscire con 1 per specificare che si è verificato un errore.
OnClose()eventi del modulo e simili.
Se ti trovi nel modulo dell'app principale, prova a utilizzare
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)anche fare il lavoro.
Enviorment.Exitè un'uscita sporca e piuttosto invasiva perché impedisce l'esecuzione del codice di pulizia dell'applicazione. La maggior parte delle volte non è la scelta giusta.
Potrei essere in ritardo alla festa, ma ecco la mia semplice soluzione e funziona a meraviglia con ogni applicazione che ho:
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
{ }
Avevo lo stesso identico problema e anch'io avevo la necessità di evitare casi duplicati: propongo una soluzione alternativa a quella proposta da HiredMind (che funzionerà bene).
Quello che sto facendo è avviare il nuovo processo con processId del vecchio processo (quello che attiva il riavvio) come argomento della riga 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);
Quindi, quando la nuova app viene avviata, prima analizzo gli argomenti della riga cm e controllo se il flag di riavvio è presente con un processId, quindi aspetto che il processo esca:
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
}
}
Ovviamente non sto mostrando tutti i controlli di sicurezza che ho in atto ecc.
Anche se non è l'ideale, trovo che sia una valida alternativa in modo da non dover disporre di un'app separata solo per gestire il riavvio.
/allowMultipleInstancesbandiera piuttosto che quella piuttosto strana /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);
Sembra funzionare meglio di Application.Restart ();
Non sono sicuro di come questo gestisca se il tuo programma protegge da più istanze. La mia ipotesi è che sarebbe meglio lanciare un secondo .exe che si interrompe e quindi avvia l'applicazione principale per te.
È semplice, devi solo chiamare i Application.Restart()metodi che tendono a richiamare la tua applicazione per il riavvio. Ma devi uscire dall'ambiente locale con i loro codici di errore:
Application.Restart();
Environment.exit(int errorcode);
è possibile creare un'enumerazione del codice di errore per utilizzarlo in modo efficace.
Un altro metodo è semplicemente uscire dall'applicazione e avviare il processo con il percorso eseguibile:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Prova questo codice:
bool appNotRestarted = true;
Questo codice deve essere anche nella funzione:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
Ho trovato un'altra soluzione, forse chiunque può usarla anche.
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
Il codice è semplificato, quindi prenditi cura delle eccezioni e altro;)
Stai dimenticando le opzioni / parametri della riga di comando che sono stati passati all'istanza attualmente in esecuzione. Se non li passi, non stai facendo un vero riavvio. Imposta il Process.StartInfocon un clone dei parametri del tuo processo, quindi inizia.
Ad esempio, se il processo è stato avviato come myexe -f -nosplash myfile.txt, il metodo verrà eseguito solo myexesenza tutti quei flag e parametri.
Volevo che la nuova applicazione si avviasse dopo la chiusura di quella vecchia.
Usare process.WaitForExit () per attendere l'arresto del proprio processo non ha senso. Sarà sempre il timeout.
Quindi, il mio approccio consiste nell'usare Application.Exit () quindi attendere, ma consentire l'elaborazione degli eventi, per un periodo di tempo. Quindi avvia una nuova applicazione con gli stessi argomenti della vecchia.
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 );
}
Puoi anche usare Restarter .
Restarter è un'applicazione che monitora e riavvia automaticamente programmi e applicazioni bloccati o bloccati. È stato originariamente sviluppato per monitorare e riavviare i server di gioco, ma farà il lavoro per qualsiasi console o programma o applicazione basata su form
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 restituisce il percorso del file .exe dell'applicazione. Seguire l'ordine delle chiamate. Potresti metterlo in una clausola try-catch.
Che ne dici di creare un file bat, eseguire il file batch prima di chiuderlo e quindi chiudere l'istanza corrente.
Il file batch fa questo:
Ecco i miei 2 centesimi:
La sequenza Avvia nuova istanza-> Chiudi istanza corrente dovrebbe funzionare anche per le applicazioni che non consentono di eseguire più copie contemporaneamente poiché in questo caso alla nuova istanza potrebbe essere passato un argomento della riga di comando che indicherà che è in corso un riavvio quindi il controllo per altre istanze in esecuzione non sarà necessario. Aspettando che la prima istanza finisca effettivamente, venga implementata anche se è assolutamente imperativo che non ci siano due istanze in esecuzione in parallelo.
Il problema dell'utilizzo di Application.Restart () è che avvia un nuovo processo ma quello "vecchio" rimane ancora. Pertanto ho deciso di uccidere il vecchio processo utilizzando il seguente frammento di codice:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
E funziona bene. Nel mio caso MATLAB e un'applicazione C # condividono lo stesso database SQLite. Se MATLAB sta utilizzando il database, l'app Form dovrebbe riavviarsi (+ Countdown) di nuovo, finché MATLAB non reimposta il suo bit di occupato nel database. (Solo per informazioni collaterali)
Temo che riavviare l'intera applicazione utilizzando Process stia affrontando il tuo problema nel modo sbagliato.
Un modo più semplice è modificare il file Program.cs per riavviare:
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;
}
}
}
Ho avuto un problema simile, ma il mio era correlato a una perdita di memoria non gestibile che non sono riuscito a trovare su un'app che deve essere eseguita 24 ore su 24, 7 giorni su 7. Con il cliente ho concordato che l'orario sicuro per riavviare l'app fosse 03:00 se il consumo di memoria era superiore al valore definito.
Ho provato Application.Restart, ma poiché sembra utilizzare un meccanismo che avvia una nuova istanza mentre è già in esecuzione, sono andato per un altro schema. Ho usato il trucco che il file system gestisce persistere fino a quando il processo che li ha creati non muore. Quindi, da L'applicazione, ho lasciato cadere il file sul disco e non l'ho Dispose()gestito. Ho usato il file per inviare l'eseguibile "me stesso" e anche la directory di partenza (per aggiungere flessibilità).
Codice:
_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()alla fine avrebbe avviato l'arresto dell'app e l'handle del file che ho usato StreamWriterqui sarebbe stato tenuto aperto fino a quando il processo non muore davvero. Poi...
Restarter.exe entra in azione. TENTA di leggere il file in modalità esclusiva, impedendogli di accedere fino a quando l'app principale non è morta, quindi avvia l'app principale, elimina il file ed esiste. Immagino che non possa essere più semplice:
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);
}
}
Uso quanto segue e fa esattamente quello che stai cercando:
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);
}
per utilizzare Come logout è necessario terminare tutte le app da Ram Cache, quindi chiudere prima l'applicazione e poi rieseguirla
// facendo clic sul pulsante Logout
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
Prendiamo ad esempio un'applicazione che:
Mentre l'applicazione non è registrata; (all'avvio) l'applicazione dovrebbe richiedere all'utente di registrare l'applicazione e creare un account di accesso.
Una volta inviata la registrazione e create le credenziali di accesso; l'applicazione dovrebbe riavviarsi, verificare la registrazione e chiedere all'utente di effettuare il login con le credenziali inserite (in modo che l'utente possa accedere a tutte le funzionalità dell'applicazione).
Problema: compilando e avviando l'applicazione da Visual Studio; nessuna delle 4 alternative seguenti non riuscirà a svolgere i compiti richiesti.
/*
* Note(s):
* Take into consideration that the lines bellow don't represent a code block.
* They are just a representation of possibilities,
* that can be used to restart the application.
*/
Application.Restart();
Application.Exit();
Environment.Exit(int errorCode);
Process.GetCurrentProcess().Kill();
Quello che succede è: dopo aver creato la registrazione, accedere e chiamare Application.Restart (); l'applicazione (stranamente) riaprirà il modulo di registrazione e salterà i dati in un database (anche se la risorsa è impostata su "Copia se più recente").
Soluzione: creazione in batch dell'applicazione è stata (per me) una prova che una qualsiasi delle righe precedenti funzionava effettivamente come previsto. Solo non durante la creazione e l'esecuzione dell'applicazione con Visual Studio.
In primo luogo proverei a creare in batch l'applicazione; eseguirlo fuori da Visual Studio e verificare se Application.Restart () funziona effettivamente come previsto.
Controllare anche ulteriori informazioni sull'argomento di questo thread: Come riavviare l'applicazione C # WinForm?