DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

アプリケーション(自分自身)を再起動する

ここでは、自分自身のWindowsフォームアプリケーションを再起動する(現在実行中のアプリケーションを終了させて、新たに起動する)方法を幾つか紹介します。

.NET Framework 2.0以降で、Application.Restartメソッドを使用する方法

.NET Framework 2.0からは、Application.Restartメソッドを使ってアプリケーションを再起動することができます。

VB.NET
コードを隠すコードを選択
'アプリケーションを再起動する
Application.Restart()
C#
コードを隠すコードを選択
//アプリケーションを再起動する
Application.Restart();

新たにアプリケーションを起動してから、終了する

Application.Restartメソッドを使えない場合は、新たにアプリケーションを起動させてから、現在のアプリケーションを終了させるという方法があります。新たにアプリケーションを起動させる方法は「外部アプリケーションを起動する」で、現在のアプリケーションを終了させる方法は「アプリケーション(自分自身)を終了させる」で説明しています。

Application.Restartメソッドも結局はこのような方法で再起動しているようです。

以下にその例を示します。

VB.NET
コードを隠すコードを選択
'コマンドライン引数を引き継ぐ
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()
C#
コードを隠すコードを選択
//コマンドライン引数を引き継ぐ
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」というメソッドで再起動できるようにしたものです。前の例のようにコマンドライン引数は引き継いでいませんので、必要ならばその処理を追加してください。

VB.NET
コードを隠すコードを選択
'エントリポイント
<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
C#
コードを隠すコードを選択
//エントリポイント
[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、終了を待機する最長時間、起動するアプリケーションの実行ファイルのパス、起動時に指定するコマンドライン引数)を設定することで、指定したアプリケーションを再起動することができます。

VB.NET
コードを隠すコードを選択
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
C#
コードを隠すコードを選択
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」で、再起動するアプリケーションと同じフォルダに存在しているものとします。

VB.NET
コードを隠すコードを選択
'プロセスの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()
C#
コードを隠すコードを選択
//プロセスの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」となってしまうためです。
  • 履歴:
  • 2016/6/19 「二重起動を禁止したアプリケーションを再起動する」のサンプルを、「二重起動を禁止する」のサンプルに従って書き換え。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。