开发C#.NET 2.0 WinForm应用程序。需要应用程序关闭并自行重启。
Application.Restart();
上述方法已被证明是不可靠的。
有什么更好的重启应用程序的方法?
Answers:
不幸的是,您不能使用Process.Start()启动当前正在运行的进程的实例。根据Process.Start()文档:“如果该进程已经在运行,则不会启动其他进程资源...”
该技术在VS调试器下可以很好地工作(因为VS做了某种魔术,导致Process.Start认为进程尚未运行),但是当不在调试器下运行时将失败。(请注意,这可能是特定于OS的-我似乎记得在某些测试中,它可以在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没有查看正在运行的OS进程的列表。本文档语句只谈论那个对象实例的的Process类。的Process类可被连接到正在运行的过程,但它也可以是在未启动状态。我认为这是一个设计问题。IMO的最佳实践是永远不要重用 Process 实例,并在创建后立即启动它。理想情况下,使用静态 Process.Start方法。这样,该文档和设计缺陷就永远不会发挥作用。
WaitForExit(1000)。但是不必等待整个过程就可以开始新的过程。这可能是您想要的其他行为,但是不需要启动新流程。
一个对我有用的简单得多的方法是:
Application.Restart();
Environment.Exit(0);
尽管事件处理程序通常会阻止应用程序关闭,但这样做可以保留命令行参数并可以正常工作。
Restart()调用尝试退出,无论如何启动新实例并返回。然后,Exit()调用将终止该过程,而不会给任何事件处理程序一个运行的机会。这两个进程都在很短的时间内运行,这对我来说不是问题,但在其他情况下也可能。
中的退出代码0Environment.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提出的方案提出了另一种解决方案(可以很好地工作)。
我正在做的是使用cmd行参数将旧进程(触发重新启动的进程)的processId启动新进程:
// 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
}
}
我显然没有显示我已进行的所有安全检查等。
即使不理想-我发现这是一个有效的替代方法,因此您不必为了处理重启而必须使用单独的应用程序。
/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();
}
您忘记了传递给当前正在运行的实例的命令行选项/参数。如果您不传递这些信息,那么您就不会进行真正的重启。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返回您的aplication .exe文件路径,请按照调用的顺序进行操作。您可能希望将其放在try-catch子句中。
这是我的2美分:
即使对于不允许同时运行多个副本的应用程序,“启动新实例”->“关闭当前实例”序列也应起作用,因为在这种情况下,可能会向新实例传递一个命令行参数,该参数将指示正在进行重新启动因此,无需检查其他实例是否正在运行。如果绝对没有两个实例并行运行,那么等待第一个实例实际完成也可以实现。
我担心使用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 AM。
我尝试了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应再次重新启动(+ Countdown),直到MATLAB重置数据库中的繁忙位为止。(仅供参考)