¿Cómo reinicio mi aplicación C # WinForm?

86

Desarrollo de una aplicación WinForm C # .NET 2.0. Necesita que la aplicación se cierre y se reinicie.

Application.Restart();

El método anterior ha demostrado ser poco confiable .

¿Cuál es una mejor forma de reiniciar la aplicación?

Adam Nofsinger
fuente
3
Tengo curiosidad sobre la necesidad de reiniciar su aplicación. Nunca antes había pensado en esa necesidad. ¿Cuáles son sus circunstancias?
JeffH
Nuestra circunstancia particular: una aplicación de reproductor multimedia que se supone que se ejecuta a través de algunas imágenes y contenido flash en un bucle. Debería ejecutarse durante días y días sin reiniciar la máquina, y no hay teclado / mouse, por lo que no hay interacción del usuario. Si el programa falla (excepción no controlada), es necesario reiniciar el programa, no salir ni mostrar un error. stackoverflow.com/questions/773768/… Vea por qué el programa sigue teniendo excepciones que no puedo evitar. :(
Adam Nofsinger
1
Al final, una solución mucho mejor para nuestra aplicación fue desarrollar una pequeña aplicación Watchdog que se inicia (si aún no se está ejecutando) desde la aplicación principal. El perro guardián simplemente verifica cada 10 segundos aproximadamente para ver si la aplicación principal todavía tiene un proceso en ejecución, y si no lo hace, inicia uno. Simple, elegante y mucho más resistente que intentar reiniciar desde la aplicación principal.
Adam Nofsinger
2
Otra razón para reiniciar es cuando cambia de idioma o ha descargado una actualización.
Andrew Truckle
1
Pero, ¿y si ... La aplicación de vigilancia falla?
Metoniem

Respuestas:

39

Desafortunadamente, no puede usar Process.Start () para iniciar una instancia del proceso que se está ejecutando actualmente. Según los documentos de Process.Start (): "Si el proceso ya se está ejecutando, no se inicia ningún recurso de proceso adicional ..."

Esta técnica funcionará bien con el depurador VS (porque VS hace algún tipo de magia que hace que Process.Start piense que el proceso aún no se está ejecutando), pero fallará cuando no se ejecute con el depurador. (Tenga en cuenta que esto puede ser específico del sistema operativo; creo recordar que en algunas de mis pruebas, funcionó en XP o Vista, pero es posible que solo recuerde haberlo ejecutado bajo el depurador).

Esta técnica es exactamente la que utilizó el último programador en el proyecto en el que estoy trabajando actualmente, y he estado tratando de encontrar una solución para esto durante bastante tiempo. Hasta ahora, solo he encontrado una solución, y me parece sucia y torpe: inicie una segunda aplicación, que espera en segundo plano a que termine la primera aplicación, luego vuelve a iniciar la primera aplicación. Estoy seguro de que funcionaría, pero qué asco.

Editar: El uso de una segunda aplicación funciona. Todo lo que hice en la segunda aplicación fue:

    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 es un ejemplo muy simplificado. El código real tiene muchas comprobaciones de cordura, manejo de errores, etc.)

HiredMind
fuente
Estoy de acuerdo con HiredMind, y de hecho fui con la misma implementación del "programa Watchdog" poco después de escribir la respuesta. Lo siento, debería haber vuelto aquí y actualizado. No creo que deba sentirse demasiado feo / asqueroso / sucio. El patrón del programa Watchdog se usa bastante.
Adam Nofsinger
En realidad, no necesita una segunda aplicación en el disco ... podría usar un script y generarlo sobre la marcha con un nombre temporal ... Creo que esto puede aliviar su sensación de culpa por tener una segunda aplicación por el simple hecho de reiniciar su propio ... O podría "emitir" una aplicación C # completa, compilarla, guardarla en el disco y ejecutarla (pensamiento sucio, sucio) ..
Loudenvier
El primer párrafo no es correcto: Process.Startno mira la lista de procesos del sistema operativo en ejecución. Esta declaración de documentación solo habla de esa instancia de objeto de la Processclase. La Processclase se puede adjuntar a un proceso en ejecución, pero también puede estar en un estado no iniciado. En mi opinión, este es un error de diseño. La mejor práctica, en mi opinión, es no reutilizar nunca una Process instancia e iniciarla inmediatamente después de su creación. Idealmente, use el Process.Startmétodo estático . Entonces, esta falla de documentación y diseño nunca entrará en juego.
usr
El código que se muestra aquí tampoco es confiable debido a WaitForExit(1000). Pero no es necesaria toda la espera para iniciar un nuevo proceso. Puede que desee un comportamiento adicional, pero no es necesario para iniciar un nuevo proceso.
usr
No sé ahora, pero hace nueve años (ejem) esto definitivamente era correcto. :-)
HiredMind
72

Un enfoque mucho más simple que funcionó para mí es:

Application.Restart();
Environment.Exit(0);

Esto conserva los argumentos de la línea de comandos y funciona a pesar de los controladores de eventos que normalmente evitarían el cierre de la aplicación.

La llamada a Restart () intenta salir, inicia una nueva instancia de todos modos y regresa. La llamada Exit () luego termina el proceso sin dar a ningún controlador de eventos la oportunidad de ejecutarse. Hay un período muy breve en el que ambos procesos se están ejecutando, lo cual no es un problema en mi caso, pero quizás en otros casos.

El código de salida 0 en Environment.Exit(0);especifica un apagado limpio. También puede salir con 1 para especificar que ocurrió un error.

EMP
fuente
Simple pero confiable. Esta debería ser la respuesta aceptada. Estaba tratando de implementar una función de cierre de sesión que vaciaría todas las variables globales y aparecería como si la aplicación acabara de iniciarse. Solo iba a mostrar el panel para iniciar sesión y restablecer todas las variables globales que contienen información importante de DB a nada. Esto hizo la vida mucho más simple. ¡Gracias!
DavidG
System.Windows.Forms.Application.Restart () también funciona para aplicaciones WPF. Probado con el sistema operativo Windows 10.
NthDeveloper
¡¿Qué ?! Estás bromeando ... ¿verdad? C # <3. Tenga en cuenta que esto por defecto no generará OnClose()eventos de formulario y similares.
F8ER
56

Si está en la forma de la aplicación principal, intente usar

System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Darqer
fuente
7
Es similar, pero diferente. Application.Salir no funcionó conmigo, y this.Close () hizo el trabajo.
Darqer
1
May Enviorment.Exit(0)también hará el trabajo.
Dwza
2
Enviorment.Exites una salida sucia y bastante invasiva porque evita que se ejecute el código de limpieza de la aplicación. No es la elección correcta la mayor parte del tiempo.
usr
16

Puede que llegue tarde a la fiesta, pero esta es mi solución simple y funciona como un encanto con cada aplicación que tengo:

        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
        { }
SolidSnake
fuente
Funciona muy bien en Win-7-x64 y Win-XP-x32. No veo nada sobre restricciones en los documentos . Esta debería ser la respuesta aceptada.
Bitterblue
@Bitterblue El enlace a los documentos que publicó dice "Si el proceso ya se está ejecutando, no se inicia ningún recurso de proceso adicional". Es la tercera línea de la sección de comentarios. Ojalá no fuera así, pero lo es.
HiredMind
@HiredMind Supongo que podríamos discutir sobre lo que significan "restricciones" para cada uno de nosotros. Pero la sección se titula "Comentarios", no "Restricciones". Entonces puedo vivir con eso. ¿Y por qué desearías que no fuera así? ¿Tuviste algún problema con eso? Porque no lo hice. Funciona perfectamente para mí.
Bitterblue
@HiredMind Esto no es nada personal ni nada, pero la idea de usar una segunda aplicación o un script solo para reiniciar mi aplicación C # me repugna. Win7 ejecuta este método en el modo de lanzamiento sin problemas hasta ahora (lo uso para reiniciar mi aplicación cuando el usuario cambia de idioma = nada importante). Por lo tanto, prefiero decirle al usuario que reinicie la aplicación manualmente que usar métodos que yo no apruebo. He usado guiones en el pasado y no me gustó en absoluto.
Bitterblue
@BitterBlue Estoy de acuerdo en que usar una segunda aplicación apesta, lo dije en mi respuesta. Pero, ¿ha probado el método de aplicación única fuera del depurador? No funcionó en algunas versiones de Windows cuando se dejó esta respuesta, y el modo Release o Debug no tuvo nada que ver con eso. Si funciona para ti, ¡genial! Pero este problema me mordió y lo que obtuve fueron cientos de usuarios enojados.
HiredMind
12

Tenía exactamente el mismo problema y yo también tenía el requisito de evitar instancias duplicadas: propongo una solución alternativa a la que propone HiredMind (que funcionará bien).

Lo que estoy haciendo es comenzar el nuevo proceso con el processId del proceso anterior (el que desencadena el reinicio) como un argumento de línea de 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);

Luego, cuando se inicia la nueva aplicación, primero analizo los argumentos de la línea cm y verifico si el indicador de reinicio está allí con un processId, luego espero a que ese proceso salga:

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 no estoy mostrando todos los controles de seguridad que tengo, etc.

Incluso si no es ideal, creo que esta es una alternativa válida para que no tenga que tener una aplicación separada solo para manejar el reinicio.

JohnIdol
fuente
1
En mi humilde opinión, hay una solución más ordenada para esto. También tengo el requisito de tener una instancia única y permitir que el usuario reinicie la aplicación (por ejemplo, cuando falla). Casi implementé su solución, sin embargo, se me ocurrió que sería mejor simplemente agregar un argumento de línea de comando diferente que permita que se ejecuten varias instancias.
Dennis
mmm, estás invirtiendo la lógica, ¡me gusta! El único problema es que si alguien descubre el argumento de la línea cmd que permite múltiples instancias, podrían suceder cosas extrañas: D
JohnIdol
Bueno, tuve la suerte de que era una función que los usuarios habían estado solicitando, por lo que era WIN-WIN. Preferiría que 'encontraran' una /allowMultipleInstancesbandera que una bastante extraña /restart.
Dennis
Sí, si permite múltiples instancias, seguramente es una mejor solución :)
JohnIdol
@JohnIdol Niza. Creo que cambiaré mi bandera a "-waitForProcessToSalir" o algo, pero por lo demás es una solución más elegante. En este momento estoy lidiando con un problema de ClickOnce al hacer referencia a un EXE de otro EXE, y esto lo resolvería.
HiredMind
6

Método de inicio / salida

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

Esto parece funcionar mejor que Application.Restart ();

No estoy seguro de cómo se maneja esto si su programa protege contra múltiples instancias. Supongo que es mejor que inicie un segundo .exe que pausa y luego inicia su aplicación principal por usted.

Adam Nofsinger
fuente
2
Eso podría no funcionar si la aplicación está protegida contra varias instancias.
majkinetor
Sí, tengo la sensación de que llamar a Application.Salir () solo hace que se agregue algún mensaje a una cola en algún lugar que deba ser bombeado, por lo que el segundo bit de código aquí en mi respuesta probablemente no funcionaría.
Adam Nofsinger
Desafortunadamente, esta técnica no funciona (¡ojalá lo hiciera! Es mucho más simple que mi solución). Funcionará dentro del depurador de Visual Studio, pero no en la práctica. Vea mi respuesta para una solución kludgy que funciona fuera del depurador.
HiredMind
HiredMind podría tener razón. Terminé optando por una solución de patrón Watchdog.
Adam Nofsinger
6

Es simple, solo necesita llamar a los Application.Restart()métodos que tienden a invocar su aplicación para que se reinicie. Pero debes tener que salir del entorno local con sus códigos de error:

Application.Restart();
Environment.exit(int errorcode);

puede crear una enumeración de código de error para que pueda usarlo de manera eficiente.
Otro método es simplemente salir de la aplicación e iniciar el proceso con una ruta ejecutable:

Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Sonu
fuente
Me funcionó bien, no necesito la aplicación de vigilancia en mi caso.
user685590
3

Prueba este código:

bool appNotRestarted = true;

Este código también debe estar en la función:

if (appNotRestarted == true) {
    appNotRestarted = false;
    Application.Restart();
    Application.ExitThread();
}
ferhatayhan
fuente
3

Descubrí otra solución, tal vez cualquiera pueda usarla también.

string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();

El código está simplificado, así que ocúpate de las excepciones y demás;)

br4inp4in
fuente
2

Está olvidando las opciones / parámetros de la línea de comandos que se pasaron a su instancia en ejecución. Si no los pasa, no está haciendo un reinicio real. Configure Process.StartInfocon un clon de los parámetros de su proceso, luego comience.

Por ejemplo, si su proceso se inició como myexe -f -nosplash myfile.txt, su método solo se ejecutaría myexesin todos esos indicadores y parámetros.

Erich Mirabal
fuente
2

Quería que la nueva aplicación se iniciara después de que se apagara la anterior.

Usar process.WaitForSalir () para esperar a que su propio proceso se apague no tiene sentido. Siempre se agotará el tiempo.

Entonces, mi enfoque es usar Application.Salir () y luego esperar, pero permitir que los eventos se procesen, durante un período de tiempo. Luego, inicie una nueva aplicación con los mismos argumentos que la anterior.

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 );
}
Mike Coxeter
fuente
2

También puedes usar Restarter .

Restarter es una aplicación que monitorea y reinicia automáticamente programas y aplicaciones bloqueados o bloqueados. Originalmente fue desarrollado para monitorear y reiniciar servidores de juegos, pero hará el trabajo para cualquier consola o programa o aplicación basada en formularios.

Daniel Rose
fuente
1
El enlace provisto ya no funciona, pero creo que esta es la misma aplicación en 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 devuelve la ruta del archivo .exe de su aplicación. Siga el orden de las llamadas. Es posible que desee colocarlo en una cláusula try-catch.

Olfredos6
fuente
1

¿Qué tal crear un archivo bat, ejecutar el archivo por lotes antes de cerrar y luego cerrar la instancia actual?

El archivo por lotes hace esto:

  1. espere en un bucle para comprobar si el proceso ha finalizado.
  2. iniciar el proceso.
ARIZONA.
fuente
Que es la aplicación de vigilancia de un hombre pobre, que describo un poco más detalladamente arriba. El beneficio clave de una aplicación de vigilancia real es que incluso manejará si la aplicación original muere sin poder hacer girar algo.
Adam Nofsinger
@AdamNofsinger Ya veo. Me perdí ese comentario. Es posible que desee editar su publicación con la solución que usa.
AZ.
1

Aquí están mis 2 centavos:

La secuencia Iniciar nueva instancia-> Cerrar instancia actual debería funcionar incluso para las aplicaciones que no permiten ejecutar varias copias simultáneamente, ya que en este caso la nueva instancia puede recibir un argumento de línea de comando que indicará que hay un reinicio en curso. por lo que no será necesario comprobar si hay otras instancias en ejecución. Esperando a que la primera instancia termine, también se implementará si es absolutamente imperativo que no se ejecuten dos instancias en paralelo.

Felix Prangishvili
fuente
1

Me temo que reiniciar toda la aplicación usando Process está abordando su problema de manera incorrecta.

Una forma más sencilla es modificar el archivo 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;
           }
        }
    }
Andrés
fuente
1
Cabe mencionar que con esta solución se puede reiniciar la aplicación X veces y luego salir de la aplicación para evitar un bucle infinito.
RooiWillie
Gracias @RooiWillie He agregado un contador para hacer precisamente eso, en caso de que el programa no pueda salir normalmente.
Andrew
0

Tuve un problema similar, pero el mío estaba relacionado con una pérdida de memoria inmanejable que no pude encontrar en una aplicación que debe ejecutarse 24 horas al día, 7 días a la semana. Con el cliente, acordé que el momento seguro para reiniciar la aplicación era a las 03:00 a. M. Si el consumo de memoria estaba por encima del valor definido.

Lo intenté Application.Restart, pero como parece usar algún mecanismo que inicia una nueva instancia mientras ya se está ejecutando, fui por otro esquema. Utilicé el truco de que los manejos del sistema de archivos persisten hasta que el proceso que los creó muere. Entonces, desde La aplicación, coloqué el archivo en el disco y noDispose() manejé. Usé el archivo para enviar el ejecutable "yo mismo" y el directorio de inicio también (para agregar flexibilidad).

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() al final iniciaría el cierre de la aplicación y el identificador de archivo que usé para StreamWriter aquí se mantendría abierto hasta que el proceso realmente muera. Entonces...

Restarter.exe entra en acción. Intenta leer el archivo en modo exclusivo, evitando que obtenga acceso hasta que la aplicación principal no esté muerta, luego inicia la aplicación principal, elimina el archivo y existe. Supongo que no puede ser más sencillo:

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);
    }
}
Daniel Mošmondor
fuente
0

Utilizo lo siguiente y hace exactamente lo que estás buscando:

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);
}
TomServo
fuente
0

para usar As logout, debe terminar todas las aplicaciones de Ram Cache, así que primero cierre la aplicación y luego vuelva a ejecutarla

// al hacer clic en el botón Cerrar sesión

foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
        {
            frm.Close();
        }
System.Diagnostics.Process.Start(Application.ExecutablePath);
Ahmed Aboelez
fuente
0

El problema de usar Application.Restart () es que inicia un nuevo proceso pero el "antiguo" aún permanece. Por lo tanto, decidí matar el proceso anterior utilizando el siguiente fragmento de código:

            if(Condition){
            Application.Restart();
            Process.GetCurrentProcess().Kill();
            }

Y funciona bien. En mi caso, MATLAB y una aplicación C # comparten la misma base de datos SQLite. Si MATLAB está usando la base de datos, la aplicación de formulario debería reiniciarse (+ Countdown) nuevamente, hasta que MATLAB restablezca su bit de ocupado en la base de datos. (Solo para información al margen)

Ricardo Fercher
fuente
0

Puede incluir su código dentro de una función y cuando sea necesario reiniciar, puede simplemente llamar a la función.

Petr Mach
fuente