C#.NET 2.0WinFormアプリケーションの開発。アプリケーションを閉じて再起動する必要があります。
Application.Restart();
アプリケーションを再起動するためのより良い方法は何ですか?
回答:
残念ながら、Process.Start()を使用して現在実行中のプロセスのインスタンスを開始することはできません。Process.Start()のドキュメントによると、「プロセスがすでに実行されている場合、追加のプロセスリソースは開始されません...」
この手法は、VSデバッガーでは正常に機能しますが(VSはProcess.Startがプロセスをまだ実行していないと見なす何らかの魔法を実行するため)、デバッガーで実行しないと失敗します。(これはOS固有である可能性があることに注意してください。一部のテストでは、XPまたはVistaで動作したことを覚えているようですが、デバッガーで実行したことを覚えているだけかもしれません。)
この手法は、私が現在取り組んでいるプロジェクトの最後のプログラマーが使用した手法とまったく同じであり、かなり前からこの回避策を見つけようとしています。これまでのところ、解決策は1つしか見つかりませんでしたが、2番目のアプリケーションを起動し、最初のアプリケーションが終了するのをバックグラウンドで待機してから、最初のアプリケーションを再起動します。私はそれがうまくいくと確信しています、しかし、うん。
編集:2番目のアプリケーションの使用は機能します。2番目のアプリで行ったのは次のとおりです。
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()呼び出しは、イベントハンドラーに実行の機会を与えずにプロセスを終了します。両方のプロセスが実行されている期間は非常に短いので、私の場合は問題ありませんが、他の場合は問題になる可能性があります。
の終了コード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()よりもうまく機能するようです。
プログラムが複数のインスタンスから保護されている場合、これがどのように処理されるかわからない。私の推測では、メインアプリケーションを一時停止してから起動する2番目の.exeを起動したほうがよいでしょう。
そのシンプルさは、Application.Restart()再起動するためにアプリケーションを呼び出す傾向があるメソッドを呼び出す必要があるだけです。ただし、エラーコードを使用してローカル環境を終了する必要があります。
Application.Restart();
Environment.exit(int errorcode);
エラーコードの列挙を作成して、効率的に使用することができます。
もう1つの方法は、アプリケーションを終了して、実行可能パスを持つプロセスを開始することです。
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();
コードは単純化されているので、例外などに注意してください;)
古いアプリケーションがシャットダウンした後、新しいアプリケーションを起動したかったのです。
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 );
}
リスタータを使用することもできます。
リスタータは、クラッシュまたはハングしたプログラムやアプリケーションを自動的に監視して再起動するアプリケーションです。もともとはゲームサーバーを監視して再起動するために開発されましたが、コンソールまたはフォームベースのプログラムやアプリケーションで機能します
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のシーケンスは、複数のコピーを同時に実行することを許可しないアプリケーションでも機能するはずです。この場合、新しいインスタンスにコマンドライン引数が渡され、再起動が進行中であることを示します。したがって、実行中の他のインスタンスをチェックする必要はありません。2つのインスタンスが並行して実行されていないことが絶対に必要な場合は、最初のインスタンスが実際に終了するのを待つことも実装されます。
Application.Restart()を使用する際の問題は、新しいプロセスを開始するが、「古い」プロセスがまだ残っていることです。そのため、次のコードスニペットを使用して古いプロセスを強制終了することにしました。
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
そして、それは適切に機能します。私の場合、MATLABとC#アプリケーションは同じSQLiteデータベースを共有しています。MATLABがデータベースを使用している場合、MATLABがデータベースのビジービットをリセットするまで、Form-Appは再起動(+カウントダウン)する必要があります。(サイド情報のためだけに)
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時間年中無休で実行する必要のあるアプリでは見つけることができなかった管理不能なメモリリークに関連していました。お客様との間で、メモリ消費量が定義された値を超えた場合、アプリを再起動する安全な時間は午前3時であることに同意しました。
試しましたがApplication.Restart、すでに実行中に新しいインスタンスを起動するメカニズムを使用しているようですので、別のスキームを選びました。私は、ファイルシステムが処理するトリックを使用して、それらを作成したプロセスが停止するまで存続します。そのため、アプリケーションから、ファイルをディスクにドロップしましたがDispose()、ハンドルはありませんでした。このファイルを使用して、「myself」実行可能ファイルと開始ディレクトリも送信しました(柔軟性を高めるため)。
コード:
_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);
たとえば、次のようなアプリケーションを考えてみましょう。
アプリケーションが登録されていない間; (起動時に)アプリケーションは、ユーザーにアプリケーションの登録とログインアカウントの作成を求めるプロンプトを表示する必要があります。
登録が送信され、ログイン資格情報が作成されると、アプリケーションを再起動し、登録を確認して、挿入された資格情報を使用してログインするようにユーザーに求める必要があります(ユーザーがすべてのアプリケーション機能にアクセスできるようにするため)。
問題: VisualStudioからアプリケーションをビルドして起動する。以下の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();を呼び出します。アプリケーションは(奇妙なことに)登録フォームを再度開き、データベース内のデータをスキップします(リソースが「新しい場合はコピー」に設定されている場合でも)。
解決: アプリケーションのバッチビルドは、(私にとって)上記の行のいずれかが実際に期待どおりに機能していることの証明でした。VisualStudioでアプリケーションをビルドして実行するときだけではありません。
そもそも、アプリケーションをバッチビルドしてみます。Visual Studioの外部で実行し、Application.Restart()が実際に期待どおりに機能するかどうかを確認します。
また、このスレッドの件名に関する詳細情報を確認してください: C#WinFormアプリケーションを再起動するにはどうすればよいですか?