Як перезапустити програму C # WinForm?


86

Розробка додатку WinForm на C # .NET 2.0. Потрібна програма, щоб закрити та перезапустити себе.

Application.Restart();

Вищевказаний метод виявився ненадійним .

Який кращий спосіб перезапустити програму?


3
Мені цікаво, що потрібно перезапустити програму. Я ніколи раніше не думав про цю потребу. Які ваші обставини?
JeffH

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

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

2
Інша причина перезапуску - це коли ви зміните мову або завантажили оновлення.
Andrew Truckle,

1
Але що, якщо ... Додаток сторожового збою вийде з ладу?
Metoniem

Відповіді:


39

На жаль, ви не можете використовувати 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, "");
    }

(Це дуже спрощений приклад. Реальний код має багато перевірки розумності, обробки помилок тощо)


Я погоджуюся з HiredMind, і насправді я пішов із тим самим впровадженням програми "Watchdog" незабаром після написання відповіді. На жаль, слід було повернутися сюди та оновити. Я б не подумав, що це повинно почуватися надто жахливо потворним / химерним / брудним. Шаблон програми Watchdog досить широко використовується.
Адам Нофсінгер

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

Перший абзац неправильний: Process.Startне розглядає список запущених процесів ОС. Ця документація твердження говорить тільки про те , що об'єкт , наприклад з Processкласу. ProcessКлас може бути приєднаний до працюючого процесу , але він також може бути в змозі Чи не розпочато. На мій погляд, це дизайн гріш. Найкраща практика, IMO, - ніколи не використовувати повторно Process екземпляр і негайно запускати його після створення. В ідеалі використовувати статичний Process.Startметод. Тоді ця недолік документації та дизайну ніколи не вступає у дію.
usr

Наведений тут код також ненадійний через WaitForExit(1000). Але все очікування не потрібно, щоб розпочати новий процес. Це може бути додатковою поведінкою, яку ви хочете, але для запуску нового процесу не потрібно.
usr

Я не знаю зараз, але дев'ять років тому (гм) це було точно. :-)
HiredMind

71

Набагато простіший підхід, який працював у мене:

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

Це зберігає аргументи командного рядка і працює, незважаючи на обробники подій, які зазвичай перешкоджають закриттю програми.

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

Код виходу 0 в Environment.Exit(0);задає чисте вимкнення. Ви також можете вийти з 1, щоб вказати, що сталася помилка.


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

System.Windows.Forms.Application.Restart () також працює для програм WPF. Тестується в ОС Windows 10.
NthDeveloper

Whaaaat ?! Ти жартуєш ... так? C # <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 не працював у мене, і це. Close () зробив роботу.
Даркер,

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 виконує цей метод у режимі випуску дотепер бездоганно (я використовую його для перезапуску програми, коли користувач змінює мову = нічого важливого). Тому я скоріше сказав би користувачеві перезапустити програму вручну, ніж використовувати методи, які я не схвалюю. Раніше я використовував сценарії, і мені це абсолютно не сподобалось.
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
ІМХО, що є більш акуратне рішення для цього. У мене також є вимога мати один примірник і дозволити користувачеві перезапустити програму (наприклад, коли вона виходить з ладу). Я майже реалізував ваше рішення, проте мені спало на думку, що було б краще просто додати інший аргумент командного рядка, який дозволяє запускати кілька екземплярів.
Dennis

ммм, ти змінюєш логіку - мені подобається! Проблема лише в тому, що якщо хтось
з’ясує

Ну, мені пощастило, що це функція, про яку запитували користувачі, тож це була 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 .

Повторний запуск - це програма, яка автоматично контролює та перезапускає збої або зависання програм і програм. Спочатку він був розроблений для моніторингу та перезапуску ігрових серверів, але він буде виконувати завдання для будь-якої консолі чи програми, що базується на формі


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 Я бачу зараз. Я пропустив цей коментар. Можливо, ви захочете відредагувати свою публікацію за допомогою того рішення, яке ви використовуєте.
AZ.

1

Ось мої 2 центи:

Послідовність Почати новий екземпляр-> Закрити поточний екземпляр повинна працювати навіть для програм, які не дозволяють запускати кілька копій одночасно, оскільки в цьому випадку новому екземпляру може бути переданий аргумент командного рядка, який вказуватиме на те, що триває перезапуск тому перевірка інших запущених екземплярів не буде необхідною. Чекаючи, поки перший екземпляр насправді закінчить мою програму, також буде реалізовано, якщо абсолютно важливо, щоб не було двох паралельних потоків.


1

Боюся, що перезапуск усієї програми за допомогою Процесу підходить до вашої проблеми неправильно.

Більш простий спосіб - змінити файл 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

У мене була подібна проблема, але моя пов’язана з некерованим витоком пам’яті, який я не зміг знайти в програмі, яка повинна працювати цілодобово та без вихідних. З клієнтом я домовився, що безпечний час для перезапуску програми - 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, так що спочатку закрийте програму, а потім перезапустіть її

// після натискання кнопки виходу

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.