开发 C# .NET 2.0 WinForm 应用程序。需要应用程序自行关闭并重新启动。
Application.Restart();
上述方法已被证明是不可靠的。
重新启动应用程序的更好方法是什么?
对我有用的更简单的方法是:
Application.Restart();
Environment.Exit(0);
尽管事件处理程序通常会阻止应用程序关闭,但这会保留命令行参数并正常工作。
Restart() 调用尝试退出,无论如何都会启动一个新实例并返回。Exit() 调用然后终止进程,而不给任何事件处理程序运行的机会。两个进程都在很短的时间内运行,这在我的情况下不是问题,但在其他情况下可能会出现问题。
退出代码 0 inEnvironment.Exit(0);
指定干净关闭。您也可以使用 1 退出以指定发生错误。
如果您处于主应用程序形式,请尝试使用
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
不幸的是,您不能使用 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, "");
}
(这是一个非常简化的例子。真实的代码有很多健全性检查、错误处理等)
我可能迟到了,但这是我的简单解决方案,它对我拥有的每个应用程序都很有效:
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 行 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
}
}
我显然没有展示我进行的所有安全检查等。
即使不理想 - 我发现这是一个有效的替代方案,因此您不必为了处理重启而安装单独的应用程序。
// 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);
我想出了另一个解决方案,也许任何人都可以使用它。
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
代码已简化,因此请注意异常和其他东西;)
试试这个代码:
bool appNotRestarted = true;
此代码也必须在函数中:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
您忘记了传递给当前正在运行的实例的命令行选项/参数。如果你不传递这些,你就没有真正重新启动。使用您的流程参数的克隆进行设置Process.StartInfo
,然后开始。
例如,如果您的进程以 启动myexe -f -nosplash myfile.txt
,则您的方法只会在myexe
没有所有这些标志和参数的情况下执行。
您也可以使用Restarter。
Restarter 是一个自动监控和重启崩溃或挂起的程序和应用程序的应用程序。它最初是为监视和重启游戏服务器而开发的,但它可以为任何基于控制台或表单的程序或应用程序完成这项工作
我希望在旧应用程序关闭后启动新应用程序。
使用 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 );
}
我担心使用 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;
}
}
}
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 即使对于不允许同时运行多个副本的应用程序也应该有效,因为在这种情况下,可能会向新实例传递一个命令行参数,该参数将指示正在重新启动因此不需要检查其他正在运行的实例。如果绝对没有两个实例并行运行是绝对必要的,那么等待第一个实例实际完成我的实现也是如此。
使用Application.Restart()的问题是,它启动了一个新进程,但“旧”进程仍然存在。因此,我决定使用以下代码片段来终止旧进程:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
而且效果很好。在我的例子中,MATLAB 和一个 C# 应用程序共享同一个 SQLite 数据库。如果 MATLAB 正在使用数据库,Form-App 应该再次重新启动(+倒计时),直到 MATLAB 重置其在数据库中的忙位。(仅供参考)
我遇到了类似的问题,但我的问题与无法管理的内存泄漏有关,我在必须 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);
}
对于使用 As logout,您需要从 Ram Cache 中终止所有应用程序,因此首先关闭应用程序,然后重新运行它
//点击注销按钮
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
您可以将代码包含在一个函数中,当需要重新启动时,您可以调用该函数。
以一个应用程序为例:
申请未注册时;(启动时)应用程序应提示用户注册应用程序并创建登录帐户。
提交注册并创建登录凭据后;应用程序应重新启动,检查注册并提示用户使用插入的凭据登录(以便用户可以访问所有应用程序功能)。
问题: 通过从 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() 是否确实按预期工作。
另请检查有关此线程主题的更多信息: 如何重新启动我的 C# WinForm 应用程序?