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

二重起動を禁止する

現在起動しているプロセスを調べる方法

VB6では二重起動を禁止するためにApp.PrevInstanceをチェックする方法が一般的でした。これに習い、「VB6のApp.PrevInstanceと同様のことをする」から考えると、次のようなコードですでにアプリケーションが起動しているか調べることができます。ただしこの方法は、プロセス名の同じ別のアプリケーションが存在すると、うまくいかない可能性があります。

VB.NET
コードを隠すコードを選択
'二重起動をチェックする
If Diagnostics.Process.GetProcessesByName( _
    Diagnostics.Process.GetCurrentProcess.ProcessName).Length > 1 Then
    'すでに起動していると判断する
    MessageBox.Show("多重起動はできません。")
End If
C#
コードを隠すコードを選択
//二重起動をチェックする
if (System.Diagnostics.Process.GetProcessesByName(
    System.Diagnostics.Process.GetCurrentProcess().ProcessName).Length > 1)
{
    //すでに起動していると判断する
    MessageBox.Show("多重起動はできません。");
}

アプリケーションの二重起動を防ぐには、このようなチェックをエントリポイント等で行い、すでに起動していると判断したらアプリケーションを終了させるようにします。

エントリポイントで二重起動のチェックをする例を以下に示します。エントリポイントについて詳しくは、こちらをご覧ください。

VB.NET
コードを隠すコードを選択
'エントリポイント
<STAThread()> _
Shared Sub Main()
    '二重起動をチェックする
    If Diagnostics.Process.GetProcessesByName( _
        Diagnostics.Process.GetCurrentProcess.ProcessName).Length > 1 Then
        'すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。")
        Return
    End If

    Application.Run(New Form1())
End Sub
C#
コードを隠すコードを選択
//エントリポイント
[STAThread]
static void Main() 
{
    //二重起動をチェックする
    if (System.Diagnostics.Process.GetProcessesByName(
        System.Diagnostics.Process.GetCurrentProcess().ProcessName).Length > 1)
    {
        //すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。");
        return;
    }

    Application.Run(new Form1());
}

Mutexを使用する方法

VB6以外では、Mutexを使用する方法が一般的です。.NET Frameworkでは、Mutexクラスが用意されていますので、これを使用します。

Mutexクラスを使用して二重起動を禁止するには、次のようにします。ここでもエントリポイントで二重起動をチェックしていますので、エントリポイントが分からないという方は、こちらをご覧ください。

VB.NET
コードを隠すコードを選択
'エントリポイント
<STAThread()> _
Shared Sub Main()
    'Mutexクラスの作成
    '"MyName"の部分を適当な文字列に変えてください
    Dim mutex As System.Threading.Mutex = New System.Threading.Mutex(False, "MyName")
    'ミューテックスの所有権を要求する
    If mutex.WaitOne(0, False) = False Then
        'すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。")
        Return
    End If

    Application.Run(New Form1())

    'ミューテックスを解放する
    mutex.ReleaseMutex()
End Sub
C#
コードを隠すコードを選択
//エントリポイント
[STAThread]
static void Main()
{
    //Mutexクラスの作成
    //"MyName"の部分を適当な文字列に変えてください
    System.Threading.Mutex mutex = new System.Threading.Mutex(false, "MyName");
    //ミューテックスの所有権を要求する
    if (mutex.WaitOne(0, false) == false)
    {
        //すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。");
        return;
    }

    Application.Run(new Form1());

    //ミューテックスを解放する
    mutex.ReleaseMutex();
}

さらに、Mutexコンストラクタの別のオーバーロードを使って、次のようにもできます。

VB.NET
コードを隠すコードを選択
'エントリポイント
<STAThread()> _
Shared Sub Main()
    Dim createdNew As Boolean
    'Mutexクラスの作成
    '"MyName"の部分を適当な文字列に変える
    Dim mutex As System.Threading.Mutex = _
        New System.Threading.Mutex(True, "MyName", createdNew)
    If createdNew = False Then
        'ミューテックスの初期所有権が付与されなかったときは
        'すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。")
        Return
    End If

    Application.Run(New Form1())

    'ミューテックスを解放する
    mutex.ReleaseMutex()
End Sub
C#
コードを隠すコードを選択
//エントリポイント
[STAThread]
static void Main()
{
    bool createdNew;
    //Mutexクラスの作成
    //"MyName"の部分を適当な文字列に変える
    System.Threading.Mutex mutex =
        new System.Threading.Mutex(true, "MyName", out createdNew);
    if (createdNew == false)
    {
        //ミューテックスの初期所有権が付与されなかったときは
        //すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。");
        return;
    }

    Application.Run(new Form1());

    //ミューテックスを解放する
    mutex.ReleaseMutex();
}
補足:.NET Framework 2.0からは、スレッドがMutexを解放せずに終了するとMutexは放棄された状態になり、Mutexを取得する次のスレッドでAbandonedMutexExceptionがスローされるようになりました。また、Mutexが破棄された場合、アプリケーションが強制終了されたことを示す場合があります。そのため、上記のMutexを使ったコードでも最後にReleaseMutexで解放するように書き換えました。書き換える前のコードは以下のようなものでした。

VB.NET
コードを隠すコードを選択
Private Shared _mutex As System.Threading.Mutex

'エントリポイント
<STAThread()> _
Shared Sub Main()
    'Mutexクラスの作成
    '"MyName"の部分を適当な文字列に変えてください
    _mutex = New System.Threading.Mutex(False, "MyName")
    'ミューテックスの所有権を要求する
    If _mutex.WaitOne(0, False) = False Then
        'すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。")
        Return
    End If

    Application.Run(New Form1())
End Sub
C#
コードを隠すコードを選択
private static System.Threading.Mutex _mutex;

//エントリポイント
[STAThread]
static void Main()
{
    //Mutexクラスの作成
    //"MyName"の部分を適当な文字列に変えてください
    _mutex = new System.Threading.Mutex(false, "MyName");
    //ミューテックスの所有権を要求する
    if (_mutex.WaitOne(0, false) == false)
    {
        //すでに起動していると判断して終了
        MessageBox.Show("多重起動はできません。");
        return;
    }

    Application.Run(new Form1());
}
このコードではMutexを静的フィールドとしていますが、これをローカル変数とした場合、うまく行かないことがあります。これは、ガベージコレクションによってローカル変数が破棄されてしまう可能性があるためです。このことは、「A mutex puzzle; single instance of application」で報告されています。

静的フィールドを使う以外に、GC.KeepAliveメソッドを使用する解決法もあります。この場合、Mainメソッドの最後でGC.KeepAliveメソッドを呼び出して、Mutexがガベージコレクションによって破棄されてしまうのを防ぎます。

Visual Studio 2005以降のVB.NETで、Visual Basicアプリケーションモデルを使用する方法

Visual Studio 2005からは、VB.NETを使用していれば、二重起動を禁止したアプリケーションを作成するのは簡単です。しかも、同じアプリケーションが後で起動したことを先に起動したアプリケーションで知ることもできますし、後で起動したアプリケーションのコマンドライン引数を取得することもできます。

以下にその手順を示します。

  1. メニューの「プロジェクト」-「プロパティ」により、プロジェクトのプロパティを表示します。
  2. 「アプリケーション」タブを選択します(デフォルトで選択されています)。
  3. 「アプリケーションフレームワークを有効にする」にチェックを入れ、有効にします。
  4. 「Windowsアプリケーションフレームワークプロパティ」の「単一インスタンスのアプリケーションを作成する」にチェックを入れます。
  5. 以上です。

このようにして作成されたアプリケーションでは、すでにアプリケーションが起動しているときにもう一つ起動させようとすると、はじめに起動しているアプリケーションがアクティブになり(ウィンドウが最小化状態のときは、元のサイズに戻されます)、後で起動されたアプリケーションはすぐに終了し、表示されません。

補足:はじめに起動しているアプリケーションをアクティブにしないようにもできます。この方法は、後述します。

後で起動されたアプリケーションのコマンドライン引数を取得する

My.Application.StartupNextInstanceイベントを使うことにより、アプリケーションが二重起動されたことを知ることができます。さらに、後で起動されたアプリケーションに指定されたコマンドライン引数を取得することもできます。

StartupNextInstanceイベントハンドラは、ApplicationEvents.vbファイルに記述するのが一般的のようです(別の場所でも問題ありません)。ApplicationEvents.vbファイルは、プロジェクトプロパティの「アプリケーション」タブにある「アプリケーションイベントの表示」をクリックすることにより表示されます。

以下にApplicationEvents.vbファイルにStartupNextInstanceイベントハンドラを記述した例を示します。ここでは、後で起動されたアプリケーションのコマンドライン引数を表示しています。さらに、StartupNextInstanceEventArgs.BringToForegroundをFalseにすることにより、はじめに起動されたアプリケーションをアクティブにしないようにしています。

VB.NET
コードを隠すコードを選択
Namespace My

    ' 次のイベントは MyApplication に対して利用できます:
    ' 
    ' Startup: アプリケーションが開始されたとき、
    '  スタートアップ フォームが作成される前に発生します。
    ' Shutdown: アプリケーション フォームがすべて閉じられた後に発生します。
    '  このイベントは、通常の終了以外の方法でアプリケーションが
    '  終了されたときには発生しません。
    ' UnhandledException: ハンドルされていない例外がアプリケーションで
    '  発生したときに発生するイベントです。
    ' StartupNextInstance: 単一インスタンス アプリケーションが起動され、
    '  それが既にアクティブであるときに発生します。 
    ' NetworkAvailabilityChanged: ネットワーク接続が接続されたとき、
    '  または切断されたときに発生します。
    Partial Friend Class MyApplication

        Private Sub MyApplication_StartupNextInstance( _
                ByVal sender As Object, _
                ByVal e As Microsoft.VisualBasic.ApplicationServices. _
                StartupNextInstanceEventArgs) _
                Handles Me.StartupNextInstance
            Console.WriteLine("二重起動されました")

            '後で起動されたアプリケーションのコマンドライン引数を表示
            For Each cmd As String In e.CommandLine
                Console.WriteLine(cmd)
            Next

            '先に起動しているアプリケーションをアクティブにしない
            e.BringToForeground = False
        End Sub
    End Class

End Namespace
  • 履歴:
  • 2004/4/4 「補足」の追加と、「補足」で説明された事項によるMutexを使用したコードの修正。
  • 2007/2/6 Visual Basicアプリケーションモデルを使用する方法を追加。
  • 2007/9/7 すべてのコードにおいてエントリポイントで二重起動をチェックするように変更。
  • 2009/6/9 「現在起動しているプロセスを調べる方法」のVB.NETのコードで、Uboundの代わりにLengthを使うように変更。「Mutexを使用する方法」で最後にReleaseMutexを呼び出すように変更。

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

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。