Как перезапустить приложение C # WinForm?


86

Разработка приложения WinForm на C # .NET 2.0. Необходимо, чтобы приложение закрылось и перезапустилось.

Application.Restart();

Вышеупомянутый метод оказался ненадежным .

Как лучше перезапустить приложение?


3
Мне любопытно, нужно ли перезапустить ваше приложение. Я никогда раньше не думал об этой нужде. Каковы ваши обстоятельства?
— JeffH

Наше конкретное обстоятельство - приложение медиаплеера, которое должно циклически запускать некоторые изображения и флэш-контент. Должен работать в течение нескольких дней без перезапуска компьютера, при этом нет клавиатуры / мыши, поэтому нет взаимодействия с пользователем. Если программа дает сбой (необработанное исключение), необходимо перезапустить программу, а не выходить из нее или отображать ошибку. stackoverflow.com/questions/773768/… Посмотрите, почему программа продолжает иметь исключения, которые я не могу предотвратить. :(
— Адам Нофсингер

1
В конце концов, гораздо лучшим решением для нашего приложения была разработка небольшого приложения Watchdog, которое запускается (если еще не запущено) из основного приложения. Сторожевой таймер просто проверяет каждые 10 секунд или около того, чтобы увидеть, есть ли в основном приложении все еще запущенный процесс, и если нет, запускает его. Просто, элегантно и намного надежнее, чем пытаться перезапустить из основного приложения.
— Адам Нофсингер

2
Еще одна причина перезагрузки - это изменение языка или загрузка обновления.
— Эндрю Тракл

1
Но что, если ... Приложение сторожевого пса вылетает?
— Metoniem

Ответы:


39

К сожалению, вы не можете использовать Process.Start () для запуска экземпляра текущего процесса. Согласно документации Process.Start (): «Если процесс уже запущен, никаких дополнительных ресурсов процесса не запускается ...»

Этот метод будет нормально работать в отладчике VS (потому что VS выполняет какое-то волшебство, заставляющее Process.Start думать, что процесс еще не запущен), но потерпит неудачу, если он не запущен в отладчике. (Обратите внимание, что это может зависеть от ОС - я, кажется, помню, что в некоторых моих тестах он работал как на XP, так и на Vista, но, возможно, я просто вспомнил, как запускал его под отладчиком.)

Именно этот метод использовался последним программистом проекта, над которым я сейчас работаю, и я уже довольно давно пытался найти обходной путь. Пока что я нашел только одно решение, и оно мне кажется грязным и непонятным: запустить второе приложение, которое в фоновом режиме ждет завершения первого приложения, а затем повторно запускает первое приложение. Я уверен, что это сработает, но, черт возьми.

Изменить: использование второго приложения работает. Все, что я сделал во втором приложении, было:

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

(Это очень упрощенный пример. В реальном коде много проверок работоспособности, обработки ошибок и т. Д.)


Я согласен с HiredMind, и я фактически сам пошел с той же реализацией «программы сторожевого пса» вскоре после написания ответа. Извините, надо было вернуться сюда и обновить. Я не думаю , что он должен чувствовать себя слишком ужасно некрасиво / противный / грязный. Шаблон программы Watchdog довольно широко используется.
— Адам Нофсингер,

На самом деле вам не нужно второе приложение на диске ... вы можете использовать сценарий и генерировать его на лету с временным именем ... Я думаю, что это может облегчить ваше чувство вины, имея второе приложение ради перезапуска свое собственное ... Или вы могли бы "выпустить" целое приложение C #, скомпилировать его, сохранить на диск и выполнить (грязное, грязное мышление) ..
— Loudenvier

Первый абзац не правильный: Process.Startне смотрит список запущенных процессов ОС. Эта документация утверждение говорит только о том , что объект , например из Processкласса. ProcessКласс может быть присоединен к работающему процессу , но он также может быть в состоянии Не начато. На мой взгляд, это промах в дизайне. Лучшая практика, IMO, - никогда не использовать Process экземпляр повторно и сразу же запускать его после создания. В идеале используйте статический Process.Startметод. Тогда этот недостаток документации и дизайна никогда не вступит в игру.
— usr

Показанный здесь код также ненадежен из-за WaitForExit(1000). Но для того, чтобы начать новый процесс, совсем не обязательно ждать. Это может быть дополнительное поведение, которое вам нужно, но оно не требуется для запуска нового процесса.
— usr

Не знаю, как сейчас, но девять лет назад (кхм) это определенно было правильно. :-)
— HiredMind

72

У меня сработал гораздо более простой подход:

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

Это сохраняет аргументы командной строки и работает, несмотря на обработчики событий, которые обычно предотвращают закрытие приложения.

Вызов Restart () пытается завершить работу, все равно запускает новый экземпляр и возвращается. Затем вызов Exit () завершает процесс, не давая возможности обработчикам событий запуститься. В течение очень короткого периода времени выполняются оба процесса, что не является проблемой в моем случае, но, возможно, в других случаях.

Код выхода 0 Environment.Exit(0);указывает на полное завершение работы. Вы также можете выйти, нажав 1, чтобы указать, что произошла ошибка.


Просто, но надежно. Это должен быть принятый ответ. Я пытался реализовать функцию выхода, которая очищала бы все глобальные переменные и выглядела так, как будто приложение только что запущено. Я собирался просто показать панель для входа в систему и сбросить все глобальные переменные, содержащие важную информацию из БД, в ничто. Это сделало жизнь намного проще. Благодарность!
— DavidG

System.Windows.Forms.Application.Restart () также работает для приложений WPF. Протестировано с ОС Windows 10.
— NthDeveloper

Что ?! Ты шутишь ... да? С # <3. Имейте в виду, что по умолчанию это не будет вызывать OnClose()события формы и тому подобное.
— F8ER

56

Если вы находитесь в основной форме приложения, попробуйте использовать

System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app

7
Это похоже, но по-другому. Application.Exit не работал у меня, и this.Close () выполнил свою работу.
— Darqer

1
Мэй Enviorment.Exit(0)тоже сделает эту работу.
— Dwza

2
Enviorment.Exit- грязный и довольно инвазивный выход, поскольку он предотвращает запуск кода очистки приложения. В большинстве случаев это не правильный выбор.
— usr

16

Возможно, я опоздаю на вечеринку, но вот мое простое решение, которое отлично работает с каждым моим приложением:

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

Очень хорошо работает на Win-7-x64 и Win-XP-x32. Я не вижу никаких ограничений в документации . Это должен быть принятый ответ.
— Bitterblue

@Bitterblue Ссылка на опубликованные вами документы гласит: «Если процесс уже запущен, дополнительные ресурсы процесса не запускаются». Это 3-я строка в разделе примечаний. Хотелось бы, чтобы этого не было, но это так.
— HiredMind

@HiredMind Я думаю, мы могли бы спорить о том, что «ограничения» значат для каждого из нас. Но раздел называется «Примечания», а не «Ограничения». Так что я могу жить с этим. И почему вы хотите, чтобы этого не было? Были ли у вас проблемы с этим? Потому что я этого не сделал. У меня работает безупречно.
— Bitterblue

@HiredMind В этом нет ничего личного или чего-то еще, но идея использования второго приложения или сценария только для перезапуска моего приложения C # отталкивает меня. Win7 до сих пор безупречно выполняет этот метод в режиме Release (я использую его для перезапуска приложения, когда пользователь меняет язык = ничего важного). Поэтому я лучше посоветую пользователю перезапустить приложение вручную, чем использовать методы, которые я не одобряю. Раньше я использовал скрипты, и мне это совершенно не нравилось.
— Bitterblue

@BitterBlue Я согласен с тем, что использование второго приложения - отстой - я сказал об этом в своем ответе. Но пробовали ли вы использовать метод одного приложения вне отладчика? Когда оставался этот ответ, он не работал в некоторых версиях Windows, и режим выпуска или отладки не имел к этому никакого отношения. Если это сработает для вас, отлично! Но меня укусила эта проблема, и я получил сотни разгневанных пользователей.
— HiredMind

12

У меня была точно такая же проблема, и у меня тоже было требование предотвращать дублирование экземпляров - я предлагаю альтернативное решение тому, которое предлагает HiredMind (которое будет работать нормально).

Что я делаю, так это запускаю новый процесс с processId старого процесса (того, который запускает перезапуск) в качестве аргумента строки 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);

Затем, когда новое приложение запускается, я сначала разбираю аргументы строки cm и проверяю, есть ли флаг перезапуска с processId, а затем жду, пока этот процесс завершится:

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

Я, очевидно, не показываю все имеющиеся у меня проверки безопасности и т. Д.

Даже если это не идеально - я считаю это допустимой альтернативой, так что вам не нужно иметь отдельное приложение только для перезагрузки.


1
ИМХО, есть более изящное решение. У меня также есть требование иметь один экземпляр и разрешить пользователю перезапускать приложение (например, при сбое). Я почти реализовал ваше решение, однако мне пришло в голову, что было бы лучше просто добавить другой аргумент командной строки, который позволяет запускать несколько экземпляров.
— Деннис

ммм, вы меняете логику - мне это нравится! Единственная проблема в том, что если кто-то выяснит аргумент строки cmd, который допускает несколько экземпляров, могут произойти странные вещи: D
— JohnIdol 01

Что ж, мне повезло, что это была функция, которую запрашивали пользователи, так что это была WIN-WIN. Я бы предпочел, чтобы они «нашли» /allowMultipleInstancesфлаг, чем довольно странный /restart.
— Деннис

Да, если вы допускаете несколько экземпляров, это наверняка лучшее решение :)
— JohnIdol

@JohnIdol Отлично. Думаю, я изменю свой флаг на "-waitForProcessToExit" или что-то в этом роде, но в остальном это более элегантное решение. Прямо сейчас я борюсь с проблемой ClickOnce со ссылкой на один EXE из другого EXE, и это решит эту проблему.
— HiredMind

6

Метод запуска / выхода

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

Кажется, это работает лучше, чем Application.Restart ();

Не знаю, как это работает, если ваша программа защищает от нескольких экземпляров. Я предполагаю, что вам лучше запустить второй .exe, который приостанавливает работу, а затем запускает ваше основное приложение.


2
Это может не сработать, если приложение защищено от нескольких экземпляров.
— majkinetor

Да, у меня такое ощущение, что вызов Application.Exit () вызывает только добавление какого-то сообщения в очередь, которую нужно перекачать, поэтому второй бит кода здесь в моем ответе, вероятно, действительно не сработает.
— Адам Нофсингер,

К сожалению, этот метод не работает (я бы хотел, чтобы он работал! Это намного проще, чем мое решение). Он будет работать внутри отладчика Visual Studio, но не на практике. См. Мой ответ о беспорядочном решении, которое работает вне отладчика.
— HiredMind,

Возможно, HiredMind прав. В итоге я остановился на решении паттерна Watchdog.
— Адам Нофсингер

6

Это просто, вам просто нужно вызвать Application.Restart()методы, которые имеют тенденцию вызывать ваше приложение для перезапуска. Но вы должны выйти из локальной среды с их кодами ошибок:

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

вы можете создать список кодов ошибок, чтобы использовать их эффективно.
Другой способ - просто выйти из приложения и запустить процесс с исполняемым путем:

Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);

Сработало хорошо для меня, в моем случае приложение сторожевого пса не нужно.
— user685590

3

Попробуйте этот код:

bool appNotRestarted = true;

Этот код также должен быть в функции:

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

3

Я придумал другое решение, возможно, кто-нибудь тоже сможет его использовать.

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

Код упрощен, поэтому позаботьтесь об исключениях и прочем;)


2

Вы забываете параметры / параметры командной строки, которые были переданы вашему запущенному в данный момент экземпляру. Если вы не пройдете их, вы не выполните настоящий перезапуск. Задайте Process.StartInfoс помощью клона параметров вашего процесса, затем выполните запуск.

Например, если ваш процесс был запущен как myexe -f -nosplash myfile.txt, ваш метод будет выполняться только myexeбез всех этих флагов и параметров.


2

Я хотел, чтобы новое приложение запускалось после закрытия старого.

Использование process.WaitForExit () для ожидания завершения вашего собственного процесса не имеет смысла. Это всегда будет тайм-аут.

Итак, мой подход - использовать Application.Exit (), а затем ждать, но разрешить обработку событий в течение определенного периода времени. Затем запустите новое приложение с теми же аргументами, что и старое.

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

Вы также можете использовать Restarter .

Restarter - это приложение, которое автоматически отслеживает и перезапускает вышедшие из строя или зависшие программы и приложения. Первоначально он был разработан для мониторинга и перезапуска игровых серверов, но он будет выполнять свою работу для любой консоли или программы или приложения на основе форм.


1
Прилагаемая ссылка больше не работает, но я думаю, что это то же приложение на 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 возвращает путь к файлу .exe приложения. Следуйте порядку вызовов. Вы можете поместить его в предложение try-catch.


1

Как насчет создания файла bat, запустить командный файл перед закрытием, а затем закрыть текущий экземпляр.

Пакетный файл делает это:

  1. подождите в цикле, чтобы проверить, завершился ли процесс.
  2. начать процесс.

Это сторожевое приложение для бедняков, о котором я немного подробнее расскажу выше. Ключевым преимуществом настоящего сторожевого приложения является то, что оно даже обработает, если исходное приложение умирает, не имея возможности что-то запустить.
— Адам Нофсингер

@AdamNofsinger Теперь я понимаю. Я пропустил этот комментарий. Возможно, вы захотите отредактировать свой пост с помощью того решения, которое вы используете.
— АЗ.

1

Вот мои 2 цента:

Последовательность Start New Instance-> Close Current Instance должна работать даже для приложений, которые не позволяют запускать несколько копий одновременно, поскольку в этом случае новому экземпляру может быть передан аргумент командной строки, который будет указывать на то, что выполняется перезапуск. поэтому проверка наличия других запущенных экземпляров не требуется. Ожидание завершения работы первого экземпляра, я тоже буду реализован, если абсолютно необходимо, чтобы никакие два экземпляра не работали параллельно.


1

Я опасаюсь, что перезапуск всего приложения с использованием Process неверно решит вашу проблему.

Более простой способ - изменить файл Program.cs для перезапуска:

    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
Стоит отметить, что с помощью этого решения вы можете перезапустить приложение X раз, а затем выйти из приложения, чтобы избежать бесконечного цикла.
— RooiWillie

Спасибо @RooiWillie, я добавил счетчик, чтобы сделать это, на случай, если программа не может нормально выйти.
— Эндрю

0

У меня была аналогичная проблема, но моя была связана с неуправляемой утечкой памяти, которую я не мог найти в приложении, которое должно работать 24/7. С клиентом я согласился, что безопасным временем для перезапуска приложения является 03:00, если потребление памяти превышает заданное значение.

Я пробовал Application.Restart, но поскольку он, кажется, использует какой-то механизм, запускающий новый экземпляр, когда он уже запущен, я выбрал другую схему. Я использовал уловку, с помощью которой файловая система обрабатывается до тех пор, пока процесс, создавший их, не умрет. Итак, из приложения я сбросил файл на диск, а не Dispose()дескриптор. Я также использовал этот файл для отправки исполняемого файла «себе» и начального каталога (для большей гибкости).

Код:

_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()в конце запустит завершение работы приложения, а дескриптор файла, который я использовал StreamWriterздесь, будет оставаться открытым до тех пор, пока процесс действительно не умрет. Затем...

В действие вступает Restarter.exe. Он ПЫТАЕТСЯ прочитать файл в монопольном режиме, не позволяя ему получить доступ, пока основное приложение не будет мертвым, затем запускает основное приложение, удаляет файл и существует. Думаю, проще не бывает:

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

Я использую следующее, и он делает именно то, что вы ищете:

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

для использования в качестве выхода из системы вам необходимо завершить все приложения из Ram Cache, поэтому сначала закройте приложение, а затем перезапустите его

// при нажатии кнопки выхода

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

0

Проблема использования Application.Restart () в том, что он запускает новый процесс, но «старый» все еще остается. Поэтому я решил убить старый процесс, используя следующий фрагмент кода:

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

И это работает как следует. В моем случае MATLAB и приложение C # используют одну и ту же базу данных SQLite. Если MATLAB использует базу данных, Form-App следует перезапустить (+ обратный отсчет) снова, пока MATLAB не сбросит свой бит занятости в базе данных. (Только для дополнительной информации)


0

Вы можете заключить свой код внутри функции, и когда потребуется перезапуск, вы можете просто вызвать функцию.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.