Como eu reinicio meu aplicativo WinForm C #?


86

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?


3
Estou curioso sobre a necessidade de reiniciar seu aplicativo. Nunca pensei sobre essa necessidade antes. Quais são as suas circunstâncias?
JeffH

Nossa circunstância particular - um aplicativo de player de mídia que supostamente executa algumas imagens e conteúdo flash em um loop. Deve funcionar por dias e dias sem uma reinicialização da máquina e não há teclado / mouse, portanto, nenhuma interação do usuário. Se o programa travar (exceção não tratada), será necessário reiniciar o programa, não sair ou exibir um erro. stackoverflow.com/questions/773768/… Veja por que o programa continua tendo exceções que não posso evitar. :(
Adam Nofsinger

1
No final, uma solução muito melhor para nosso aplicativo foi desenvolver um pequeno aplicativo Watchdog que é iniciado (se ainda não estiver em execução) a partir do aplicativo principal. O watchdog simplesmente verifica a cada 10 segundos ou mais para ver se o aplicativo principal ainda tem um processo em execução e, se não tiver, ele inicia um. Simples, elegante e muito mais resistente do que tentar reiniciar a partir do aplicativo principal.
Adam Nofsinger

2
Outro motivo para reiniciar é quando você altera o idioma ou baixa uma atualização.
Andrew Truckle

1
Mas e se ... O aplicativo watchdog travar?
Metoniem

Respostas:


39

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.)


Eu concordo com a HiredMind e, na verdade, comecei a mesma implementação do "programa Watchdog" logo após escrever a resposta. Desculpe, deveria ter voltado aqui e atualizado. Eu não acho que ele deve se sentir muito horrivelmente feia / nojento / sujo. O padrão do programa Watchdog é amplamente utilizado.
Adam Nofsinger

Na verdade, você não precisa de um segundo aplicativo no disco ... você pode usar um script e gerá-lo em tempo real com um nome temporário ... Acho que isso pode aliviar seu sentimento de culpa por ter um segundo aplicativo para reiniciar seu próprio ... Ou você pode "emitir" um aplicativo C # inteiro, compilá-lo, salvá-lo no disco e executá-lo (pensamento sujo, sujo) ..
Loudenvier

O primeiro parágrafo não está correto: 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.
usr

O código mostrado aqui também não é confiável por causa de 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.
usr

Não sei agora, mas nove anos atrás (ahem) isso estava definitivamente correto. :-)
HiredMind

72

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.


Simples, mas confiável. Esta deve ser a resposta aceita. Eu estava tentando implementar um recurso de logout que esvaziaria todas as variáveis ​​globais e pareceria como se o aplicativo tivesse acabado de iniciar. Eu ia apenas mostrar o painel para fazer login e redefinir todas as variáveis ​​globais que contêm informações importantes do banco de dados para nada. Isso tornou a vida muito mais simples. Obrigado!
DavidG

System.Windows.Forms.Application.Restart () também funciona para aplicativos WPF. Testado com Windows 10 OS.
NthDeveloper

O quê ?! Você está brincando ... certo? C # <3. Tenha em mente que isso, por padrão, não gerará OnClose()eventos de formulário e similares.
F8ER

56

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

7
É semelhante, mas diferente. Application.Exit não funcionou comigo, e this.Close () fez o trabalho.
Darqer de

1
Maio Enviorment.Exit(0)também fará o trabalho.
Dwza

2
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.
usr

16

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
        { }

Funciona muito bem no Win-7-x64 e Win-XP-x32. Não vejo nada sobre quaisquer restrições nos documentos . Esta deve ser a resposta aceita.
Bitterblue

@Bitterblue O link para os documentos que você postou afirma "Se o processo já estiver em execução, nenhum recurso de processo adicional será iniciado." É a 3ª linha na seção de comentários. Eu gostaria que não fosse assim, mas é.
HiredMind

@HiredMind Acho que poderíamos discutir sobre o que "restrições" significa para cada um de nós. Mas a seção é intitulada "Observações" e não "Restrições". Então eu posso viver com isso. E por que você deseja que não seja assim? Você teve algum problema com isso? Porque eu não fiz. Funciona perfeitamente para mim.
Bitterblue

@HiredMind Isso não é nada pessoal, mas a ideia de usar um segundo aplicativo ou um script apenas para reiniciar meu aplicativo C # é repulsivo para mim. O Win7 executa esse método no modo Release perfeitamente até agora (eu o uso para reiniciar meu aplicativo quando o usuário muda de idioma = nada importante). Portanto, prefiro dizer ao usuário para reiniciar o aplicativo manualmente do que usar métodos que eu não aprovo. Já usei scripts no passado e não gostei totalmente.
Bitterblue

@BitterBlue Eu concordo que usar um segundo aplicativo é uma merda - eu disse isso na minha resposta. Mas você já tentou o método de aplicativo único fora do depurador? Não funcionou em algumas versões do Windows quando deixou esta resposta, e o modo Release ou Debug não teve nada a ver com isso. Se isso funciona pra você, ótimo! Mas fui mordido por esse problema e o que consegui foram centenas de usuários irritados.
HiredMind

12

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.


1
IMHO que existe uma solução mais limpa para isso. Eu também tenho um requisito de ter uma única instância e permitir que o usuário reinicie o aplicativo (por exemplo, quando ele travar). Quase implementei sua solução, no entanto, ocorreu-me que seria melhor simplesmente adicionar um argumento de linha de comando diferente que permite a execução de várias instâncias.
Dennis

mmm, você está invertendo a lógica - eu gosto! O único problema é que se alguém descobrir o argumento da linha cmd que permite várias instâncias, coisas estranhas podem acontecer: D
JohnIdol

Bem, eu tive a sorte de ser um recurso que os usuários estavam solicitando, então era WIN-WIN. Eu preferiria que eles 'encontrassem' uma /allowMultipleInstancesbandeira do que aquela estranha /restart.
Dennis

Sim, se você permitir várias instâncias, essa é uma solução melhor com certeza :)
JohnIdol 01 de

@JohnIdol Nice. Acho que vou mudar meu sinalizador para "-waitForProcessToExit" ou algo assim, mas caso contrário, essa é uma solução mais elegante. No momento, estou lutando com um problema do ClickOnce com referência a um EXE de outro EXE, e isso resolveria o problema.
HiredMind

6

Método de início / saída

// 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ê.


2
Isso pode não funcionar se o aplicativo estiver protegido de várias instâncias.
majkinetor

Sim, tenho a sensação de que chamar Application.Exit () apenas faz com que alguma mensagem seja adicionada a uma fila em algum lugar que precise ser bombeada, então o segundo bit de código aqui em minha resposta provavelmente não funcionaria.
Adam Nofsinger

Infelizmente, essa técnica não funciona (gostaria que funcionasse! É muito mais simples do que a minha solução). Ele funcionará dentro do depurador do Visual Studio, mas não na prática. Veja minha resposta para uma solução complicada que funciona fora do depurador.
HiredMind,

HiredMind pode estar certo. Acabei optando por uma solução padrão de Watchdog.
Adam Nofsinger

6

É 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);

Funcionou bem para mim, não há necessidade do app watchdog no meu caso.
user685590

3

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();
}

3

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;)


2

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.


2

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 );
}

2

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


1
O link fornecido não funciona mais, mas acho que este é o mesmo aplicativo no CNET: download.cnet.com/Restarter/3000-2094_4-75810552.html
Surfbutler

2
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.


1

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:

  1. espere em um loop para verificar se o processo foi encerrado.
  2. iniciar o processo.

Que é uma aplicação de vigilância do pobre, que descrevo um pouco mais detalhadamente acima. O principal benefício de um aplicativo watchdog real é que ele até mesmo controlará se o aplicativo original morrer sem ser capaz de girar algo.
Adam Nofsinger

@AdamNofsinger eu vejo agora. Eu perdi esse comentário. Você pode querer editar sua postagem com a solução que você usa.
AZ.

1

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.


1

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;
           }
        }
    }

1
Vale ressaltar que com esta solução você pode reiniciar o aplicativo X vezes e, a partir daí, encerrar o aplicativo para evitar um loop infinito.
RooiWillie

Obrigado @RooiWillie. Adicionei um contador para fazer exatamente isso, caso o programa não saia normalmente.
Andrew

0

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);
    }
}

0

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);
}

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);

0

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)


0

Você pode colocar seu código dentro de uma função e quando for necessário reiniciar você pode simplesmente chamar a função.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.