C#を用いてデスクトップアプリケーションを開発する際、同じアプリケーションを二重に起動させたくないという要件は頻繁に発生します。

ファイルを操作するアプリケーションや、データベースへの排他的なアクセスが必要なツールでは、多重起動を許容するとデータの不整合を招く恐れがあります。

本記事では、Windows環境で最も一般的かつ堅牢な「Mutex (ミューテックス)」を利用した多重起動防止の実装方法について、2026年現在の最新の.NET環境に即して解説します。

多重起動を防止すべき理由とメリット

デスクトップアプリにおいて多重起動を制御することは、システムの安定性とユーザー体験の向上に直結します。

まず、システムリソースの競合を回避できるという大きなメリットがあります。

特定のログファイルやローカルDBに対して複数のプロセスが同時に書き込みを試みると、アクセス権限エラーやデータの破損が発生するリスクが高まります。

また、ユーザーにとっても、すでに起動しているアプリを二重に立ち上げてしまうことは、混乱や誤操作の原因となります。

多重起動を適切に防止し、必要に応じて既存のウィンドウを前面に表示させることで、洗練されたアプリケーションを提供することが可能になります。

Mutex (ミューテックス) とは何か

Mutexとは、複数のプロセス間で同期をとるためのオペレーティングシステム (OS) レベルのオブジェクトです。

名前付きMutexを作成すると、その名称はシステム全体で一意のものとして管理されます。

あるプロセスが特定の名前でMutexを作成している場合、後から起動した別のプロセスは同じ名前のMutexを作成、または所有することができません。

この仕組みを利用することで、「システム内に特定の名前のオブジェクトが既に存在するかどうか」を判定基準として、二重起動を検知できます。

C#では、System.Threading 名前空間にある Mutex クラスを使用することで、簡単にこの機能を実装できます。

Mutexを用いた実装手順

ここでは、最も基本的なコンソールアプリケーションやWindows Forms、WPFで利用できる実装コードを紹介します。

ポイントは、Mainメソッドの開始直後でMutexのチェックを行うことです。

C#
using System;
using System.Threading;
using System.Windows.Forms;

namespace MultiInstancePrevention
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            // アプリケーション固有の一意な名前を指定します。
            // GUIDなどを使用すると重複の可能性を最小限に抑えられます。
            string mutexName = "MyUniqueAppIdentifier-12345-67890";

            // Mutexの所有権を要求し、新規作成されたかどうかを判定します。
            // usingを使用することで、スコープを抜けた際に確実にリソースを解放します。
            using (Mutex mutex = new Mutex(false, mutexName, out bool createdNew))
            {
                if (!createdNew)
                {
                    // すでにMutexが存在する場合、多重起動とみなして終了します。
                    MessageBox.Show("アプリケーションは既に起動しています。", "二重起動防止");
                    return;
                }

                // 正常に起動する場合の処理を記述します。
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new MainForm());
            }
        }
    }
}
実行結果
(2回目にアプリを起動した場合)
ダイアログ表示:「アプリケーションは既に起動しています。」

コードの解説とポイント

new Mutex(false, mutexName, out bool createdNew) の第3引数である createdNew が重要です。

これが true であれば、その名前のMutexが初めてシステムに登録されたことを意味し、自分が最初のインスタンスであることが保証されます。

逆に false であれば、既に他のプロセスが同じ名前のMutexを保持しているため、二重起動であると判断できます。

mutexName には、他のアプリケーションと重複しない名前を付ける必要があります。

一般的には、プロジェクトのプロパティから確認できる「アセンブリGUID」を使用するのが最も安全です。

グローバルMutexとローカルMutexの使い分け

Mutexの名前の先頭に特定の接頭辞を付けることで、その影響範囲を制御できます。

Windowsのターミナルサービスやマルチユーザー環境(リモートデスクトップなど)では、この違いを理解しておく必要があります。

接頭辞種類挙動
Local\ローカルMutex現在のユーザーセッション内でのみ多重起動を防止します。
Global\グローバルMutex全てのユーザーセッションをまたいで、システム全体で多重起動を防止します。

通常、デスクトップアプリであれば Global\ 接頭辞を付けておくことで、どのユーザーが起動してもシステム全体で1つだけに制限できるようになります。

ただし、グローバルMutexを使用する場合は、実行ユーザーに適切な権限が必要になる場合があるため注意してください。

ガベージコレクション (GC) への対策

Mutexを実装する際、多くの初心者が陥る罠が「ガベージコレクションによる意図しない解放」です。

Mutex クラスのインスタンスがローカル変数として宣言され、その後参照されなくなった場合、アプリの実行中にGCによって回収されてしまうことがあります。

Mutexが回収されると、OSレベルのハンドルも解放され、他のプロセスが起動可能になってしまいます。

これを防ぐためには、先ほどの実装例のように using ブロックで囲むか、あるいはクラスの静的フィールドとして保持し続ける必要があります。

「アプリケーションが動作している間は、Mutexオブジェクトがメモリ上に存在し続けなければならない」という原則を忘れないでください。

既存のインスタンスを前面に表示させる方法

単にアプリを終了させるだけでなく、既に起動しているウィンドウを最前面に持ってくる処理を追加すると、ユーザーの利便性が飛躍的に向上します。

これには Windows API (User32.dll) の SetForegroundWindowShowWindow を使用します。

C#
using System.Runtime.InteropServices;
using System.Diagnostics;

// 外部関数の宣言
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

private const int SW_RESTORE = 9;

static void ActivateExistingInstance()
{
    Process current = Process.GetCurrentProcess();
    foreach (Process process in Process.GetProcessesByName(current.ProcessName))
    {
        // 自分自身を除外
        if (process.Id != current.Id)
        {
            // ウィンドウハンドルを取得してアクティブ化
            IntPtr handle = process.MainWindowHandle;
            if (handle != IntPtr.Zero)
            {
                ShowWindow(handle, SW_RESTORE);
                SetForegroundWindow(handle);
            }
            break;
        }
    }
}

Mutexで多重起動を検知した際にこの ActivateExistingInstance を呼び出すことで、ユーザーに対して「既にここにあるよ」と親切に示すことができます。

ただし、プロセス名だけで判定すると同名の別プログラムと競合する可能性があるため、厳密には実行ファイルのパスを比較するなどの工夫が必要です。

.NET 10 (2026年最新) 環境における考慮事項

2026年現在の最新 .NET 環境においても、Mutex クラスの基本的な使い方は変わっていません。

しかし、モダンな .NET アプリケーションでは「Top-level statements (最上位レベルのステートメント)」が主流となっており、従来の Main メソッドが見えにくくなっています。

最上位レベルのステートメントを使用している場合でも、ファイルの先頭で Mutex を作成し、スクリプト全体の終了時まで生存させる構造は同じです。

また、.NET 9以降ではネイティブ AOT (Ahead-Of-Time) コンパイルが一般化していますが、Mutexを使用した同期は AOT 環境でも問題なく動作します。

プラットフォームの互換性についても考慮が必要です。

Mutex は Windows 固有の仕組みに強く依存しているため、macOS や Linux で動作するクロスプラットフォームアプリを開発する場合は、ファイルロックなどの代替手段を検討する必要があります。

その他の代替手法:プロセスチェック

Mutexを使用しない方法として、実行中のプロセス一覧をスキャンする手法も存在します。

Process.GetProcessesByName を使うことで、現在実行中の同じ名前のプロセスを数えることができます。

実装はより直感的ですが、「プロセスが起動してから一覧に反映されるまでのタイムラグ」があるため、同時に2つ起動された場合に両方が「自分以外はいない」と判断してしまう競合状態 (Race Condition) が発生する可能性があります。

そのため、高い信頼性が求められるシステムでは、OSが原子性を保証している Mutex の使用が推奨されます。

まとめ

C#でアプリケーションの多重起動を防止する方法として、Mutexを用いた実装は最も標準的で信頼性の高い選択肢です。

適切な名前(GUID等)を付けた Mutex オブジェクトを Main メソッドで生成し、createdNew フラグによって起動の可否を判断しましょう。

実装の際には、ガベージコレクションによる意図しない解放を防ぐためにオブジェクトの生存期間を正しく管理することが不可欠です。

また、Windows APIを組み合わせて既存のウィンドウをアクティブ化する処理を加えることで、より完成度の高いアプリケーションを構築できます。

2026年の .NET 環境でも、これらの基本原則は依然として通用する重要なスキルです。

本記事を参考に、安全でユーザーフレンドリーなデスクトップアプリケーションの開発に取り組んでみてください。