Разработка приложения WinForm на C # .NET 2.0. Необходимо, чтобы приложение закрылось и перезапустилось.
Application.Restart();
Вышеупомянутый метод оказался ненадежным .
Как лучше перезапустить приложение?
Разработка приложения WinForm на C # .NET 2.0. Необходимо, чтобы приложение закрылось и перезапустилось.
Application.Restart();
Вышеупомянутый метод оказался ненадежным .
Как лучше перезапустить приложение?
Ответы:
К сожалению, вы не можете использовать 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, "");
}
(Это очень упрощенный пример. В реальном коде много проверок работоспособности, обработки ошибок и т. Д.)
Process.Startне смотрит список запущенных процессов ОС. Эта документация утверждение говорит только о том , что объект , например из Processкласса. ProcessКласс может быть присоединен к работающему процессу , но он также может быть в состоянии Не начато. На мой взгляд, это промах в дизайне. Лучшая практика, IMO, - никогда не использовать Process экземпляр повторно и сразу же запускать его после создания. В идеале используйте статический Process.Startметод. Тогда этот недостаток документации и дизайна никогда не вступит в игру.
WaitForExit(1000). Но для того, чтобы начать новый процесс, совсем не обязательно ждать. Это может быть дополнительное поведение, которое вам нужно, но оно не требуется для запуска нового процесса.
У меня сработал гораздо более простой подход:
Application.Restart();
Environment.Exit(0);
Это сохраняет аргументы командной строки и работает, несмотря на обработчики событий, которые обычно предотвращают закрытие приложения.
Вызов Restart () пытается завершить работу, все равно запускает новый экземпляр и возвращается. Затем вызов Exit () завершает процесс, не давая возможности обработчикам событий запуститься. В течение очень короткого периода времени выполняются оба процесса, что не является проблемой в моем случае, но, возможно, в других случаях.
Код выхода 0 Environment.Exit(0);указывает на полное завершение работы. Вы также можете выйти, нажав 1, чтобы указать, что произошла ошибка.
OnClose()события формы и тому подобное.
Если вы находитесь в основной форме приложения, попробуйте использовать
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)тоже сделает эту работу.
Enviorment.Exit- грязный и довольно инвазивный выход, поскольку он предотвращает запуск кода очистки приложения. В большинстве случаев это не правильный выбор.
Возможно, я опоздаю на вечеринку, но вот мое простое решение, которое отлично работает с каждым моим приложением:
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
{ }
У меня была точно такая же проблема, и у меня тоже было требование предотвращать дублирование экземпляров - я предлагаю альтернативное решение тому, которое предлагает 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
}
}
Я, очевидно, не показываю все имеющиеся у меня проверки безопасности и т. Д.
Даже если это не идеально - я считаю это допустимой альтернативой, так что вам не нужно иметь отдельное приложение только для перезагрузки.
/allowMultipleInstancesфлаг, чем довольно странный /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);
Кажется, это работает лучше, чем Application.Restart ();
Не знаю, как это работает, если ваша программа защищает от нескольких экземпляров. Я предполагаю, что вам лучше запустить второй .exe, который приостанавливает работу, а затем запускает ваше основное приложение.
Это просто, вам просто нужно вызвать Application.Restart()методы, которые имеют тенденцию вызывать ваше приложение для перезапуска. Но вы должны выйти из локальной среды с их кодами ошибок:
Application.Restart();
Environment.exit(int errorcode);
вы можете создать список кодов ошибок, чтобы использовать их эффективно.
Другой способ - просто выйти из приложения и запустить процесс с исполняемым путем:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Попробуйте этот код:
bool appNotRestarted = true;
Этот код также должен быть в функции:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
Я придумал другое решение, возможно, кто-нибудь тоже сможет его использовать.
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
Код упрощен, поэтому позаботьтесь об исключениях и прочем;)
Вы забываете параметры / параметры командной строки, которые были переданы вашему запущенному в данный момент экземпляру. Если вы не пройдете их, вы не выполните настоящий перезапуск. Задайте Process.StartInfoс помощью клона параметров вашего процесса, затем выполните запуск.
Например, если ваш процесс был запущен как myexe -f -nosplash myfile.txt, ваш метод будет выполняться только myexeбез всех этих флагов и параметров.
Я хотел, чтобы новое приложение запускалось после закрытия старого.
Использование 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 );
}
Вы также можете использовать Restarter .
Restarter - это приложение, которое автоматически отслеживает и перезапускает вышедшие из строя или зависшие программы и приложения. Первоначально он был разработан для мониторинга и перезапуска игровых серверов, но он будет выполнять свою работу для любой консоли или программы или приложения на основе форм.
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.
Как насчет создания файла bat, запустить командный файл перед закрытием, а затем закрыть текущий экземпляр.
Пакетный файл делает это:
Вот мои 2 цента:
Последовательность Start New Instance-> Close Current Instance должна работать даже для приложений, которые не позволяют запускать несколько копий одновременно, поскольку в этом случае новому экземпляру может быть передан аргумент командной строки, который будет указывать на то, что выполняется перезапуск. поэтому проверка наличия других запущенных экземпляров не требуется. Ожидание завершения работы первого экземпляра, я тоже буду реализован, если абсолютно необходимо, чтобы никакие два экземпляра не работали параллельно.
Я опасаюсь, что перезапуск всего приложения с использованием 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;
}
}
}
У меня была аналогичная проблема, но моя была связана с неуправляемой утечкой памяти, которую я не мог найти в приложении, которое должно работать 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);
}
}
Я использую следующее, и он делает именно то, что вы ищете:
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);
}
для использования в качестве выхода из системы вам необходимо завершить все приложения из Ram Cache, поэтому сначала закройте приложение, а затем перезапустите его
// при нажатии кнопки выхода
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
Проблема использования Application.Restart () в том, что он запускает новый процесс, но «старый» все еще остается. Поэтому я решил убить старый процесс, используя следующий фрагмент кода:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
И это работает как следует. В моем случае MATLAB и приложение C # используют одну и ту же базу данных SQLite. Если MATLAB использует базу данных, Form-App следует перезапустить (+ обратный отсчет) снова, пока MATLAB не сбросит свой бит занятости в базе данных. (Только для дополнительной информации)