Розробка додатку WinForm на C # .NET 2.0. Потрібна програма, щоб закрити та перезапустити себе.
Application.Restart();
Вищевказаний метод виявився ненадійним .
Який кращий спосіб перезапустити програму?
Розробка додатку WinForm на C # .NET 2.0. Потрібна програма, щоб закрити та перезапустити себе.
Application.Restart();
Вищевказаний метод виявився ненадійним .
Який кращий спосіб перезапустити програму?
Відповіді:
На жаль, ви не можете використовувати Process.Start () для запуску екземпляра поточно запущеного процесу. Згідно з документами Process.Start (): "Якщо процес уже запущений, додатковий ресурс процесу не запускається ..."
Цей прийом чудово працюватиме під налагоджувачем VS (оскільки VS робить якусь магію, яка змушує Process.Start думати, що процес ще не запущений), але не вдасться, якщо не буде запущений під налагоджувачем. (Зверніть увагу, що це може бути специфічно для ОС - я, схоже, пам’ятаю, що під час мого тестування він працював або на XP, або на Vista, але я, можливо, просто пам’ятаю, як запускав його під налагоджувачем.)
Ця техніка саме та, яку використовував останній програміст проекту, над яким я зараз працюю, і я вже давно намагаюся знайти обхідний шлях для цього. Поки що я знайшов лише одне рішення, і воно мені просто здається брудним і брудним: запустіть 2-у програму, яка чекає у фоновому режимі, поки не закінчиться перша програма, а потім знову запустить 1-ю програму. Я впевнений, що це спрацювало б, але, ну.
Редагувати: працює друге додаток. У другому додатку я зробив лише:
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 .
Повторний запуск - це програма, яка автоматично контролює та перезапускає збої або зависання програм і програм. Спочатку він був розроблений для моніторингу та перезапуску ігрових серверів, але він буде виконувати завдання для будь-якої консолі чи програми, що базується на формі
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 центи:
Послідовність Почати новий екземпляр-> Закрити поточний екземпляр повинна працювати навіть для програм, які не дозволяють запускати кілька копій одночасно, оскільки в цьому випадку новому екземпляру може бути переданий аргумент командного рядка, який вказуватиме на те, що триває перезапуск тому перевірка інших запущених екземплярів не буде необхідною. Чекаючи, поки перший екземпляр насправді закінчить мою програму, також буде реалізовано, якщо абсолютно важливо, щоб не було двох паралельних потоків.
Боюся, що перезапуск усієї програми за допомогою Процесу підходить до вашої проблеми неправильно.
Більш простий спосіб - змінити файл 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;
}
}
}
У мене була подібна проблема, але моя пов’язана з некерованим витоком пам’яті, який я не зміг знайти в програмі, яка повинна працювати цілодобово та без вихідних. З клієнтом я домовився, що безпечний час для перезапуску програми - 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, так що спочатку закрийте програму, а потім перезапустіть її
// після натискання кнопки виходу
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 не скине свій зайнятий біт у базі даних. (Лише для побічної інформації)