ここでは、自分自身のWindowsフォームアプリケーションを再起動する(現在実行中のアプリケーションを終了させて、新たに起動する)方法を幾つか紹介します。
.NET Framework 2.0からは、Application.Restartメソッドを使ってアプリケーションを再起動することができます。
'アプリケーションを再起動する
Application.Restart()
//アプリケーションを再起動する
Application.Restart();
Application.Restartメソッドを使えない場合は、新たにアプリケーションを起動させてから、現在のアプリケーションを終了させるという方法があります。新たにアプリケーションを起動させる方法は「外部アプリケーションを起動する」で、現在のアプリケーションを終了させる方法は「アプリケーション(自分自身)を終了させる」で説明しています。
Application.Restartメソッドも結局はこのような方法で再起動しているようです。
以下にその例を示します。
'コマンドライン引数を引き継ぐ Dim cmd As String = "" Dim args As String() = Environment.GetCommandLineArgs() For i As Integer = 1 To args.Length - 1 If 1 < i Then cmd += " " End If cmd += """" & args(i) & """" Next '新たにアプリケーションを起動する System.Diagnostics.Process.Start(Application.ExecutablePath, cmd) '現在のアプリケーションを終了する Application.Exit()
//コマンドライン引数を引き継ぐ string cmd = ""; string[] args = Environment.GetCommandLineArgs(); for (int i = 1; i < args.Length; i++) { if (1 < i) { cmd += " "; } cmd += "\"" + args[i] + "\""; } //新たにアプリケーションを起動する System.Diagnostics.Process.Start(Application.ExecutablePath, cmd); //現在のアプリケーションを終了する Application.Exit();
上の例ではアプリケーションを終了させるためにApplication.Exitメソッドを使用していますので、例えばフォームのFormClosingイベントでフォームを閉じるのがキャンセルされた時などはアプリケーションが終了しません。これは、Application.Restartメソッドも同様です。
補足:.NET Framework 2.0からは、Application.Exitメソッドでの終了がキャンセルされたかを知ることができます。この方法を使えば、終了がキャンセルされた場合は新たなアプリケーションを起動しないようにすることもできます。詳しくは、「アプリケーション(自分自身)を終了させる」をご覧ください。
補足:Application.Exitメソッドの代わりにForm.CloseメソッドやEnvironment.Exitメソッドを使った方がうまくいくという報告も「.net - How do I restart my C# WinForm Application? - Stack Overflow」にあります。
上記の方法は新たにアプリケーションを起動してから現在のアプリケーションを終了させていますので、二重起動を禁止したアプリケーション(単一インスタンスのアプリケーション)では再起動できない恐れがあります。
二重起動を禁止したアプリケーションを再起動させるには、例えば「二重起動を禁止する」で紹介しているMutexを使用した方法の場合、MutexをReleaseMutexメソッドで解放した後で新たなアプリケーションを起動します。
以下に具体例を示します。この例は「二重起動を禁止する」で紹介しているサンプルを書き換えて、「RestartApplication」というメソッドで再起動できるようにしたものです。前の例のようにコマンドライン引数は引き継いでいませんので、必要ならばその処理を追加してください。
'エントリポイント <STAThread> _ Shared Sub Main() 'Mutex名を決める(必ずアプリケーション固有の文字列に変更すること!) Dim mutexName As String = "MyApplicationName" 'Mutexオブジェクトを作成する Dim mutex As New System.Threading.Mutex(False, mutexName) Dim hasHandle As Boolean = False Try Try 'ミューテックスの所有権を要求する hasHandle = mutex.WaitOne(0, False) '.NET Framework 2.0以降の場合 Catch ex As System.Threading.AbandonedMutexException '別のアプリケーションがミューテックスを解放しないで終了した時 hasHandle = True End Try 'ミューテックスを得られたか調べる If hasHandle = False Then '得られなかった場合は、すでに起動していると判断して終了 MessageBox.Show("多重起動はできません。") Return End If 'はじめからMainメソッドにあったコードを実行 Application.EnableVisualStyles() Application.SetCompatibleTextRenderingDefault(False) Application.Run(New Form1()) Finally If hasHandle Then 'ミューテックスを解放する mutex.ReleaseMutex() End If mutex.Close() End Try 'restartingがTrueの場合は終了前に起動する If restarting Then System.Diagnostics.Process.Start(Application.ExecutablePath) End If End Sub Private Shared restarting As Boolean = False ''' <summary> ''' アプリケーションを再起動する ''' </summary> Public Shared Sub RestartApplication() 'restartingをTrueにして、アプリケーションを終了させる restarting = True Application.Exit() End Sub
//エントリポイント [STAThread] static void Main() { //Mutex名を決める(必ずアプリケーション固有の文字列に変更すること!) string mutexName = "MyApplicationName"; //Mutexオブジェクトを作成する System.Threading.Mutex mutex = new System.Threading.Mutex(false, mutexName); bool hasHandle = false; try { try { //ミューテックスの所有権を要求する hasHandle = mutex.WaitOne(0, false); } //.NET Framework 2.0以降の場合 catch (System.Threading.AbandonedMutexException) { //別のアプリケーションがミューテックスを解放しないで終了した時 hasHandle = true; } //ミューテックスを得られたか調べる if (hasHandle == false) { //得られなかった場合は、すでに起動していると判断して終了 MessageBox.Show("多重起動はできません。"); return; } //はじめからMainメソッドにあったコードを実行 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } finally { if (hasHandle) { //ミューテックスを解放する mutex.ReleaseMutex(); } mutex.Close(); } //restartingがTrueの場合は終了前に起動する if (restarting) { System.Diagnostics.Process.Start(Application.ExecutablePath); } } private static bool restarting = false; /// <summary> /// アプリケーションを再起動する /// </summary> public static void RestartApplication() { //restartingをTrueにして、アプリケーションを終了させる restarting = true; Application.Exit(); }
今まで紹介した方法はすべて、現在のアプリケーションが終了する前に新たなアプリケーションを起動しています。しかし本来ならば、現在のアプリケーションが完全に終了した後で新たに起動したいところです。
そのためには、再起動用のアプリケーションを別に作成します。つまり、現在のアプリケーションから再起動用のアプリケーションを起動し、再起動用のアプリケーションでは現在のアプリケーションが終了したことを確認してから新たなアプリケーションを起動するようにします。
このような再起動用のアプリケーションの例を示します。コマンドライン引数に様々な情報(終了を監視するプロセスのID、終了を待機する最長時間、起動するアプリケーションの実行ファイルのパス、起動時に指定するコマンドライン引数)を設定することで、指定したアプリケーションを再起動することができます。
Public Class Program 'エントリポイント Public Shared Sub Main() Try Restart() Catch ex As Exception System.Windows.Forms.MessageBox.Show( _ "再起動に失敗しました。" & vbCrLf & vbCrLf & _ "エラー: " + ex.Message, _ "再起動に失敗しました", _ System.Windows.Forms.MessageBoxButtons.OK, _ System.Windows.Forms.MessageBoxIcon.Error) End Try End Sub 'コマンドライン引数で指定された設定を使用して再起動する Private Shared Sub Restart() 'コマンドライン引数を取得する Dim args As String() = System.Environment.GetCommandLineArgs() If args.Length < 4 Then Throw New ArgumentException("コマンドライン引数が足りません。") End If '終了を監視するプロセスIDを取得する Dim processId As Integer Try processId = Integer.Parse(args(1)) Catch ex As Exception Throw New ArgumentException("プロセスIDが不正です。", ex) End Try '終了の最長待機時間を取得する Dim waitTime As Integer Try waitTime = Integer.Parse(args(2)) Catch ex As Exception Throw New ArgumentException("待機時間が不正です。", ex) End Try If waitTime < 1 OrElse 60000 < waitTime Then Throw New ArgumentException("待機時間は最長1分です。") End If '起動するアプリケーションの実行ファイルのパスを取得する Dim exePath As String = args(3) If Not System.IO.File.Exists(exePath) Then Throw New ArgumentException("実行ファイルが見つかりません。") End If '起動時に指定するコマンドライン引数を作成する Dim cmd As String = "" For i As Integer = 4 To args.Length - 1 If 4 < i Then cmd += " " End If cmd += """" & args(i) & """" Next '再起動する Restart(processId, waitTime, exePath, cmd) End Sub ''' <summary> ''' 指定された設定を使用して再起動する ''' </summary> ''' <param name="exitProcessId">終了まで待機するアプリケーション</param> ''' <param name="exitWaitTime">待機する再長時間(ミリ秒単位)</param> ''' <param name="exePath">再起動する実行ファイルのパス</param> ''' <param name="commandLine">再起動する時指定するコマンドライン引数</param> Private Shared Sub Restart(exitProcessId As Integer, _ exitWaitTime As Integer, _ exePath As String, _ commandLine As String) '終了を監視するプロセスを探す Dim p As System.Diagnostics.Process Try p = System.Diagnostics.Process.GetProcessById(exitProcessId) Catch generatedExceptionName As ArgumentException '見つからない時はアプリケーションがすでに終了していると判断する p = Nothing End Try 'アプリケーションの終了を待機する If Not p Is Nothing Then If Not p.WaitForExit(exitWaitTime) Then Throw New Exception("アプリケーションが終了しませんでした。") End If p.Close() End If 'アプリケーションを起動する System.Diagnostics.Process.Start(exePath, commandLine) End Sub End Class
using System; public class Program { //エントリポイント public static void Main() { try { Restart(); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show( "再起動に失敗しました。\n\nエラー: " + ex.Message, "再起動に失敗しました", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); } } //コマンドライン引数で指定された設定を使用して再起動する private static void Restart() { //コマンドライン引数を取得する string[] args = System.Environment.GetCommandLineArgs(); if (args.Length < 4) { throw new ArgumentException("コマンドライン引数が足りません。"); } //終了を監視するプロセスIDを取得する int processId; try { processId = int.Parse(args[1]); } catch (Exception ex) { throw new ArgumentException("プロセスIDが不正です。", ex); } //終了の最長待機時間を取得する int waitTime; try { waitTime = int.Parse(args[2]); } catch (Exception ex) { throw new ArgumentException("待機時間が不正です。", ex); } if (waitTime < 1 || 60000 < waitTime) { throw new ArgumentException("待機時間は最長1分です。"); } //起動するアプリケーションの実行ファイルのパスを取得する string exePath = args[3]; if (!System.IO.File.Exists(exePath)) { throw new ArgumentException("実行ファイルが見つかりません。"); } //起動時に指定するコマンドライン引数を作成する string cmd = ""; for (int i = 4; i < args.Length; i++) { if (4 < i) { cmd += " "; } cmd += "\"" + args[i] + "\""; } //再起動する Restart(processId, waitTime, exePath, cmd); } /// <summary> /// 指定された設定を使用して再起動する /// </summary> /// <param name="exitProcessId">終了まで待機するアプリケーション</param> /// <param name="exitWaitTime">待機する再長時間(ミリ秒単位)</param> /// <param name="exePath">再起動する実行ファイルのパス</param> /// <param name="commandLine">再起動する時指定するコマンドライン引数</param> private static void Restart(int exitProcessId, int exitWaitTime, string exePath, string commandLine) { //終了を監視するプロセスを探す System.Diagnostics.Process p; try { p = System.Diagnostics.Process.GetProcessById(exitProcessId); } catch (ArgumentException) { //見つからない時はアプリケーションがすでに終了していると判断する p = null; } //アプリケーションの終了を待機する if (p != null) { if (!p.WaitForExit(exitWaitTime)) { throw new Exception("アプリケーションが終了しませんでした。"); } p.Close(); } //アプリケーションを起動する System.Diagnostics.Process.Start(exePath, commandLine); } }
補足:コンソールアプリケーションとしてビルドすると、起動時にコマンドプロンプトが表示されます。Windowsアプリケーションとしてビルドすると、表示されなくなります。
次に、この再起動用アプリケーションを使用してアプリケーションを起動する例を示します。ここでは、再起動用アプリケーションの実行ファイル名が「restart.exe」で、再起動するアプリケーションと同じフォルダに存在しているものとします。
'プロセスのIDを取得する Dim processId As Integer = System.Diagnostics.Process.GetCurrentProcess().Id 'アプリケーションが終了するまで待機する時間 Dim waitTime As Integer = 30000 'コマンドライン引数を作成する Dim cmd As String = """" & processId.ToString() & """ " & _ """" & waitTime.ToString() & """ " & _ Environment.CommandLine '再起動用アプリケーションのパスを取得する Dim restartPath As String = System.IO.Path.Combine( _ Application.StartupPath, "restart.exe") '再起動用アプリケーションを起動する System.Diagnostics.Process.Start(restartPath, cmd) 'アプリケーションを終了する Application.Exit()
//プロセスのIDを取得する int processId = System.Diagnostics.Process.GetCurrentProcess().Id; //アプリケーションが終了するまで待機する時間 int waitTime = 30000; //コマンドライン引数を作成する string cmd = "\"" + processId.ToString() + "\" " + "\"" + waitTime.ToString() + "\" " + Environment.CommandLine; //再起動用アプリケーションのパスを取得する string restartPath = System.IO.Path.Combine( Application.StartupPath, "restart.exe"); //再起動用アプリケーションを起動する System.Diagnostics.Process.Start(restartPath, cmd); //アプリケーションを終了する Application.Exit();
補足:Visual Studioのデバッグで上記のコードを実行しても、アプリケーションは再起動しません。この時、Environment.CommandLineで取得できる実行ファイルの名前が「(アセンブリ名).vshost.exe」となってしまうためです。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。