Desenvolvendo um aplicativo WinForm C # .NET 2.0. O aplicativo precisa ser fechado e reiniciado.
Application.Restart();
O método acima provou não ser confiável .
Qual é a melhor maneira de reiniciar o aplicativo?
Desenvolvendo um aplicativo WinForm C # .NET 2.0. O aplicativo precisa ser fechado e reiniciado.
Application.Restart();
O método acima provou não ser confiável .
Qual é a melhor maneira de reiniciar o aplicativo?
Respostas:
Infelizmente, você não pode usar Process.Start () para iniciar uma instância do processo em execução no momento. De acordo com os documentos Process.Start (): "Se o processo já estiver em execução, nenhum recurso de processo adicional será iniciado ..."
Essa técnica funcionará bem com o depurador VS (porque o VS faz algum tipo de mágica que faz com que Process.Start pense que o processo ainda não está em execução), mas falhará quando não for executado no depurador. (Observe que isso pode ser específico do sistema operacional - pareço me lembrar que em alguns dos meus testes, funcionou no XP ou no Vista, mas posso apenas me lembrar de executá-lo no depurador.)
Essa técnica é exatamente a usada pelo último programador no projeto no qual estou trabalhando atualmente, e estou tentando encontrar uma solução alternativa para isso há algum tempo. Até agora, só encontrei uma solução, e ela me parece suja e desajeitada: inicie um segundo aplicativo, que espera em segundo plano pelo encerramento do primeiro aplicativo, em seguida, reinicia o primeiro aplicativo. Tenho certeza de que funcionaria, mas, eca.
Editar: Usar um segundo aplicativo funciona. Tudo que fiz no segundo aplicativo foi:
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, "");
}
(Este é um exemplo muito simplificado. O código real tem muitas verificações de sanidade, tratamento de erros, etc.)
Process.Startnão olha a lista de processos do sistema operacional em execução. Esta declaração de documentação fala apenas sobre aquela instância de objeto da Processclasse. A Processclasse pode ser anexada a um processo em execução, mas também pode estar em um estado não iniciado. Na minha opinião, este é um problema de design. A melhor prática, IMO, é nunca reutilizar uma Process instância e iniciá-la imediatamente após a criação. Idealmente, use o Process.Startmétodo estático . Então, essa falha de documentação e design nunca entra em jogo.
WaitForExit(1000). Mas toda a espera não é necessária para iniciar um novo processo. Pode ser um comportamento adicional que você deseja, mas não é necessário para iniciar um novo processo.
Uma abordagem muito mais simples que funcionou para mim é:
Application.Restart();
Environment.Exit(0);
Isso preserva os argumentos da linha de comando e funciona apesar dos manipuladores de eventos que normalmente impediriam o fechamento do aplicativo.
A chamada Restart () tenta sair, inicia uma nova instância de qualquer maneira e retorna. A chamada Exit () então termina o processo sem dar a nenhum manipulador de eventos a chance de ser executado. Há um período muito breve em que os dois processos estão rodando, o que não é um problema no meu caso, mas talvez em outros casos.
O código de saída 0 em Environment.Exit(0);especifica um desligamento limpo. Você também pode sair com 1 para especificar que ocorreu um erro.
OnClose()eventos de formulário e similares.
Se você estiver no aplicativo principal, tente usar
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)também fará o trabalho.
Enviorment.Exité uma saída suja e bastante invasiva porque evita que o código de limpeza do aplicativo seja executado. Não é a escolha certa na maioria das vezes.
Posso chegar atrasado à festa, mas aqui está minha solução simples e funciona perfeitamente com todos os aplicativos que tenho:
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
{ }
Eu tive exatamente o mesmo problema e também tinha um requisito para evitar instâncias duplicadas - eu proponho uma solução alternativa para a que o HiredMind está propondo (que funcionará bem).
O que estou fazendo é iniciar o novo processo com o processId do processo antigo (aquele que aciona a reinicialização) como um argumento de linha 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);
Então, quando o novo aplicativo é iniciado, primeiro analiso os argumentos da linha cm e verifico se o sinalizador de reinicialização está lá com um processId e, em seguida, aguardo a saída desse processo:
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
}
}
Obviamente, não estou mostrando todas as verificações de segurança que tenho em vigor, etc.
Mesmo que não seja ideal - considero esta uma alternativa válida para que você não precise ter um aplicativo separado apenas para lidar com a reinicialização.
/allowMultipleInstancesbandeira do que aquela estranha /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);
Isso parece funcionar melhor do que Application.Restart ();
Não tenho certeza de como isso funciona se o seu programa protege contra várias instâncias. Meu palpite é que seria melhor iniciar um segundo .exe que pausa e, em seguida, inicia seu aplicativo principal para você.
É simples, você só precisa chamar os Application.Restart()métodos que tendem a invocar seu aplicativo para ser reiniciado. Mas você deve sair do ambiente local com seus códigos de erro:
Application.Restart();
Environment.exit(int errorcode);
você pode criar uma enumeração de código de erro para que você possa usá-lo com eficácia.
Outro método é apenas sair do aplicativo e iniciar o processo com o caminho executável:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Experimente este código:
bool appNotRestarted = true;
Este código também deve estar na função:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
Eu descobri uma outra solução, talvez qualquer um possa usá-la também.
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
O código é simplificado, então tome cuidado com as exceções e outras coisas;)
Você está esquecendo as opções / parâmetros da linha de comando que foram passados para a sua instância em execução no momento. Se você não os passar, não estará reiniciando de verdade. Colocou oProcess.StartInfo com um clone dos parâmetros do seu processo e comece.
Por exemplo, se seu processo foi iniciado como myexe -f -nosplash myfile.txt, seu método só seria executado myexesem todos esses sinalizadores e parâmetros.
Eu queria que o novo aplicativo fosse iniciado depois que o antigo fosse encerrado.
Usar process.WaitForExit () para aguardar o encerramento de seu próprio processo não faz sentido. Sempre vai expirar.
Portanto, minha abordagem é usar Application.Exit () e esperar, mas permitir que os eventos sejam processados, por um período de tempo. Em seguida, inicie um novo aplicativo com os mesmos argumentos do antigo.
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 );
}
Você também pode usar o Restarter .
Restarter é um aplicativo que monitora e reinicia automaticamente programas e aplicativos travados ou travados. Ele foi originalmente desenvolvido para monitorar e reiniciar servidores de jogos, mas fará o trabalho para qualquer console ou programa ou aplicativo baseado em formulário
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 retorna o caminho do arquivo .exe da aplicação. Siga a ordem das chamadas. Você pode querer colocá-lo em uma cláusula try-catch.
Que tal criar um arquivo bat, executar o arquivo em lote antes de fechar e, em seguida, fechar a instância atual.
O arquivo em lote faz isso:
Aqui estão meus 2 centavos:
A sequência Iniciar Nova Instância-> Fechar Instância Atual deve funcionar mesmo para os aplicativos que não permitem a execução de várias cópias simultaneamente, pois neste caso a nova instância pode receber um argumento de linha de comando que indicará que há uma reinicialização em andamento portanto, a verificação de outras instâncias em execução não será necessária. Aguardando que a primeira instância realmente termine minha implementação também se for absolutamente necessário que duas instâncias não estejam sendo executadas em paralelo.
Temo que reiniciar todo o aplicativo usando Process esteja abordando seu problema da maneira errada.
Uma maneira mais fácil é modificar o arquivo Program.cs para reiniciar:
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;
}
}
}
Eu tive um problema semelhante, mas o meu estava relacionado a um vazamento de memória incontrolável que não consegui encontrar em um aplicativo que funciona 24 horas por dia, 7 dias por semana. Combinei com o cliente que o horário seguro para reiniciar o aplicativo seria às 03h00 se o consumo de memória estivesse acima do valor definido.
Tentei Application.Restart, mas como parece usar algum mecanismo que inicia uma nova instância enquanto já está em execução, optei por outro esquema. Eu usei o truque que o sistema de arquivos controla e persiste até que o processo que os criou morra. Então, do aplicativo, coloquei o arquivo no disco e não Dispose()o manipulei. Usei o arquivo para enviar o executável para mim e também o diretório inicial (para adicionar flexibilidade).
Código:
_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()no final, iniciaria o desligamento do aplicativo e o identificador de arquivo que usei StreamWriteraqui seria mantido aberto até que o processo realmente morresse. Então...
Restarter.exe entra em ação. Ele TENTA ler o arquivo em modo exclusivo, impedindo-o de obter acesso até que o aplicativo principal não esteja morto, então inicia o aplicativo principal, apaga o arquivo e existe. Acho que não pode ser mais simples:
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);
}
}
Eu uso o seguinte e faz exatamente o que você está procurando:
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);
}
para usar como logout, você precisa encerrar todos os aplicativos do Ram Cache, então feche o aplicativo primeiro e execute-o novamente
// ao clicar no botão Logout
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
O problema de usar Application.Restart () é que ele inicia um novo processo, mas o "antigo" ainda permanece. Portanto, decidi matar o processo antigo usando o seguinte trecho de código:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
E funciona bem. No meu caso, o MATLAB e um aplicativo C # estão compartilhando o mesmo banco de dados SQLite. Se o MATLAB estiver usando o banco de dados, o Form-App deve reiniciar (+ Contagem regressiva) novamente, até que o MATLAB reinicie seu bit de ocupado no banco de dados. (Apenas para informações secundárias)