【问题标题】:How do I restart my C# WinForm Application?如何重新启动我的 C# WinForm 应用程序?
【发布时间】:2010-10-21 05:36:13
【问题描述】:

开发 C# .NET 2.0 WinForm 应用程序。需要应用程序自行关闭并重新启动。

Application.Restart();

上述方法有proven to be unreliable

重新启动应用程序的更好方法是什么?

【问题讨论】:

  • 我很好奇是否需要重新启动您的应用程序。我以前从未考虑过这种需求你的情况是什么?
  • 我们的特殊情况 - 一个媒体播放器应用程序,它应该循环运行一些图像和 Flash 内容。应该在没有机器重启的情况下运行数天,并且没有键盘/鼠标,因此没有用户交互。如果程序崩溃(未处理的异常),需要重新启动程序,不能退出或显示错误。 stackoverflow.com/questions/773768/… 看看为什么程序总是出现我无法阻止的异常。 :(
  • 最后,对我们的应用程序更好的解决方案是开发一个从主应用程序启动(如果尚未运行)的小型 Watchdog 应用程序。看门狗每隔 10 秒左右检查一次,看看主应用程序是否还有一个进程在运行,如果没有,它就会启动一个。比尝试从主应用重新启动更简单、优雅且坚固。
  • 另一个重启的原因是您更改了语言或下载了更新。
  • 但是如果……看门狗应用程序崩溃了怎么办?

标签: c# .net


【解决方案1】:

很简单,你只需要调用Application.Restart() 方法,这将调用你的应用程序重新启动。您还必须使用错误代码退出本地环境:

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

您可以创建错误代码的枚举,以便您的应用程序有效退出。
另一种方法是直接退出应用程序并使用可执行路径启动进程:

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

【讨论】:

  • 对我来说效果很好,在我的情况下不需要看门狗应用程序。
【解决方案2】:

以一个应用程序为例:

  1. 当应用程序未注册时; (启动时)应用程序应提示用户注册应用程序并创建登录帐户。

  2. 提交注册并创建登录凭据后;应用程序应重新启动,检查注册并提示用户使用插入的凭据登录(以便用户可以访问所有应用程序功能)。

问题: 通过从 Visual Studio 构建和启动应用程序;以下 4 种替代方法中的任何一种都无法完成所需的任务。

/*
 * Note(s):
 * Take into consideration that the lines bellow don't represent a code block.
 * They are just a representation of possibilities,
 * that can be used to restart the application.
 */

Application.Restart();
Application.Exit();
Environment.Exit(int errorCode);
Process.GetCurrentProcess().Kill();

会发生什么:创建注册后,登录并调用 Application.Restart();应用程序将(奇怪地)重新打开注册表并跳过数据库中的数据(即使资源设置为“如果较新则复制”)。

解决方案: 批量构建应用程序(对我而言)证明上述任何行实际上都按预期工作。 只是在使用 Visual Studio 构建和运行应用程序时不会。

首先我会尝试批量构建应用程序;在 Visual Studio 之外运行它并检查 Application.Restart() 是否确实按预期工作。

另请查看有关此主题的更多信息: How do I restart my C# WinForm Application?

【讨论】:

    【解决方案3】:

    您可以将您的代码包含在一个函数中,当需要重新启动时,您可以调用该函数。

    【讨论】:

      【解决方案4】:

      我担心使用 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;
                 }
              }
          }
      

      【讨论】:

      • 值得一提的是,使用此解决方案,您可以重新启动应用程序 X 次,然后退出应用程序以避免无限循环。
      • 感谢@RooiWillie 我已经添加了一个计数器来做到这一点,以防程序无法正常退出。
      【解决方案5】:

      对我有用的更简单的方法是:

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

      这保留了命令行参数,并且尽管事件处理程序通常会阻止应用程序关闭。

      Restart() 调用尝试退出,无论如何都会启动一个新实例并返回。 Exit() 调用然后终止进程,而不给任何事件处理程序运行的机会。两个进程都在很短的时间内运行,在我的情况下这不是问题,但在其他情况下可能会出现问题。

      Environment.Exit(0); 中的退出代码 0 指定完全关闭。你也可以用 1 退出来指定发生了错误。

      【讨论】:

      • 简单但可靠。这应该是公认的答案。我试图实现一个注销功能,该功能将清空所有全局变量并看起来好像应用程序刚刚启动。我打算只显示用于登录的面板并将所有保存数据库重要信息的全局变量重置为空。这让生活变得简单多了。谢谢!
      • System.Windows.Forms.Application.Restart() 也适用于 WPF 应用程序。使用 Windows 10 操作系统测试。
      • 哇?!你在开玩笑……对吧? C# OnClose() 表单事件和类似事件。
      【解决方案6】:

      使用 Application.Restart() 的问题是,它启动了一个新进程,但“旧”进程仍然存在。因此我决定使用以下代码 sn-p 杀死旧进程:

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

      而且效果很好。在我的例子中,MATLAB 和一个 C# 应用程序共享同一个 SQLite 数据库。如果 MATLAB 正在使用数据库,Form-App 应该再次重新启动(+倒计时),直到 MATLAB 重置其在数据库中的忙位。 (仅供参考)

      【讨论】:

        【解决方案7】:
        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 子句中。

        【讨论】:

          【解决方案8】:

          我想出了另一种解决方案,也许任何人都可以使用它。

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

          代码已简化,因此请注意异常和其他内容;)

          【讨论】:

            【解决方案9】:

            为了使用 As logout,您需要从 Ram Cache 中终止所有应用程序 所以先关闭应用程序然后重新运行它

            //点击退出按钮时

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

            【讨论】:

              【解决方案10】:

              我使用以下内容,它完全符合您的要求:

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

              【讨论】:

                【解决方案11】:

                你也可以使用Restarter

                Restarter 是一个自动监控和重启崩溃或挂起的程序和应用程序的应用程序。它最初是为监视和重启游戏服务器而开发的,但它可以为任何基于控制台或表单的程序或应用程序完成这项工作

                【讨论】:

                【解决方案12】:

                试试这个代码:

                bool appNotRestarted = true;
                

                此代码也必须在函数中:

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

                【讨论】:

                  【解决方案13】:

                  我希望新应用程序在旧应用程序关闭后启动。

                  使用 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 );
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    这是我的 2 美分:

                    序列 Start New Instance->Close Current Instance 即使对于不允许同时运行多个副本的应用程序也应该有效,因为在这种情况下,新实例可能会传递一个命令行参数,该参数将指示存在正在重新启动,因此无需检查其他正在运行的实例。如果绝对没有两个实例并行运行是绝对必要的,那么等待第一个实例实际完成我的实现也是如此。

                    【讨论】:

                      【解决方案15】:

                      如何创建一个bat文件,在关闭之前运行批处理文件,然后关闭当前实例。

                      批处理文件这样做:

                      1. 循环等待,检查进程是否退出。
                      2. 开始进程。

                      【讨论】:

                      • 这是一个穷人的看门狗应用程序,我在上面描述得更详细一点。真正的看门狗应用程序的主要好处是,它甚至可以处理原始应用程序死机而无法剥离某些东西的情况。
                      • @AdamNofsinger 我现在看到了。我错过了那个评论。您可能希望使用您使用的解决方案编辑您的帖子。
                      【解决方案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 上运行良好。我在the docs 中看不到任何约束。这应该是公认的答案。
                      • @Bitterblue 您发布的文档的链接指出“如果进程已经在运行,则不会启动其他进程资源。”这是备注部分的第 3 行。我希望不是这样,但确实如此。
                      • @HiredMind 我想我们可以争论“约束”对我们每个人意味着什么。但是该部分的标题是“备注”而不是“约束”。所以我可以忍受它。为什么你希望不是这样?你有没有遇到任何问题?因为我没有。对我来说完美无缺。
                      • @HiredMind 这不是个人或任何事情,只是使用第二个应用程序或脚本来重新启动我的 C# 应用程序的想法对我来说是排斥的。到目前为止,Win7 在发布模式下完美地执行了此方法(当用户更改语言时,我使用它来重新启动我的应用程序 = 没什么重要的)。所以我宁愿告诉用户手动重启应用,也不愿使用我自己不认可的方法。我过去使用过脚本,但我完全不喜欢它。
                      • @BitterBlue 我同意使用第二个应用程序很糟糕 - 我在回答中这么说。但是你试过调试器之外的单应用方法吗?离开这个答案时,它在某些版本的 Windows 上不起作用,并且发布或调试模式与它无关。如果它对你有用,那就太好了!但我被这个问题所困扰,我得到的是数百名愤怒的用户。
                      【解决方案17】:

                      我遇到了类似的问题,但我的问题与无法管理的内存泄漏有关,我在必须 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);
                          }
                      }
                      

                      【讨论】:

                        【解决方案18】:

                        很遗憾,您不能使用 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 的观点,实际上我在写完答案后不久就自己使用了相同的“看门狗程序”实现。对不起,应该回到这里并更新。我不认为它应该感到丑陋/恶心/肮脏。看门狗程序模式被广泛使用。
                        • 您实际上不需要磁盘上的第二个应用程序...您可以使用脚本并使用临时名称动态生成它...我认为这可以减轻您的内疚感为了重新启动你自己的第二个应用程序......或者你可以“发出”整个 C# 应用程序,编译它,将它保存到磁盘并执行它(肮脏,肮脏的想法)..
                        • 第一段不正确:Process.Start 不查看正在运行的 OS 进程列表。此文档声明仅讨论 Process 类的 object instanceProcess 类可以附加到正在运行的进程,但它也可以处于未启动状态。在我看来,这是一个设计失误。 IMO 的最佳做法是永远不要重用 Process 实例并在创建后立即启动它。理想情况下,使用静态Process.Start 方法。然后,这个文档和设计缺陷永远不会发挥作用。
                        • 这里显示的代码也不可靠,因为WaitForExit(1000)。但是整个等待并不是启动一个新进程的必要条件。这可能是您想要的其他行为,但它不是启动新进程所必需的。
                        • 我现在不知道,但九年前(咳咳)这绝对是正确的。 :-)
                        【解决方案19】:

                        如果您处于主应用程序形式,请尝试使用

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

                        【讨论】:

                        • 相似,但不同。 Application.Exit 对我不起作用,而 this.Close() 完成了这项工作。
                        • May Enviorment.Exit(0) 也将完成这项工作。
                        • Enviorment.Exit 是一个肮脏且极具侵入性的退出,因为它会阻止应用程序清理代码运行。大多数时候不是正确的选择。
                        【解决方案20】:

                        我也遇到了同样的问题,而且我也有防止重复实例的要求 - 我提出了一个替代解决方案来替代 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 行 args 并检查是否存在带有 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
                           }
                        }
                        

                        我显然没有展示我已经进行的所有安全检查等。

                        即使不理想 - 我发现这是一个有效的替代方案,因此您不必为了处理重启而安装单独的应用程序。

                        【讨论】:

                        • 恕我直言,有一个更简洁的解决方案。我还要求拥有单个实例并允许用户重新启动应用程序(例如,当它崩溃时)。我几乎实现了您的解决方案,但我想到最好简单地添加一个 允许 多个实例运行的不同命令行参数。
                        • 嗯,你在颠倒逻辑——我喜欢它!唯一的问题是,如果有人找出允许多个实例的 cmd 行参数,可能会发生奇怪的事情:D
                        • 好吧,我很幸运,这是用户一直要求的功能,所以它是双赢的。我宁愿他们“找到”一个/allowMultipleInstances 标志,而不是相当奇怪的/restart 标志。
                        • 是的,如果您允许多个实例,那肯定是更好的解决方案:)
                        • @JohnIdol 不错。我想我会将我的标志更改为“-waitForProcessToExit”或其他东西,否则这是一个更优雅的解决方案。现在我正在努力解决从另一个 EXE 引用一个 EXE 的 ClickOnce 问题,这将解决这个问题。
                        【解决方案21】:

                        启动/退出方法

                        // 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.Exit() 只会将一些消息添加到需要泵送的队列中,所以我的答案中的第二段代码可能确实会不工作。
                        • 不幸的是,这种技术不起作用(我希望它起作用!它比我的解决方案简单得多)。它可以在 Visual Studio 调试器中工作,但实际上不能。请参阅我的答案,了解在调试器之外工作的笨拙解决方案。
                        • HiredMind 可能是对的。我最终选择了看门狗模式解决方案。
                        【解决方案22】:

                        您忘记了传递给当前运行实例的命令行选项/参数。如果你不传递这些,你就没有真正重新启动。使用您的进程参数的克隆设置Process.StartInfo,然后启动。

                        例如,如果您的进程以myexe -f -nosplash myfile.txt 启动,则您的方法将只执行myexe 而没有所有这些标志和参数。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-10-24
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多