連載
» 2015年11月18日 05時00分 UPDATE

.NET TIPS:WPF:多重起動を禁止するには?[C#/VB]

WPFアプリの多重起動を禁止する方法にはいくつかある。本稿ではシステムにグローバルなセマフォを利用してこれを行う方法を紹介する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象:.NET 3.0以降


 WPFアプリケーションを同時に一つしか起動されたくない場合があるだろう。例えば、アプリケーションを複数起動されると処理が競合して不整合が発生するような場合だ。それには、Windowsシステムにグローバルな(=どのプロセスからもアクセスできる)オブジェクトが利用できる。本稿では、セマフォを使う方法を紹介しよう。

ミューテックスとセマフォ

 プロセス間の排他制御に利用できる二つの仕組みがある。ミューテックスとセマフォだ。

  • ミューテックス:
    Mutexクラス(System.Threading名前空間)を介して利用する
    .NET 1.0から利用可能
    同時に実行できるのは1スレッドのみ
    実行権の取得と解放は同じスレッドで行わねばならない
  • セマフォ:
    Semaphoreクラス(System.Threading名前空間)を介して利用する
    .NET 2.0から利用可能
    同時に実行できるスレッドの数を指定できる
    実行権の取得と解放は異なるスレッドで行ってもよい

 WPFでは、取得と解放を別のスレッドでも行えるセマフォがお勧めだ。

 なお、ミューテックスを使って多重起動を禁止する方法は、Windowsフォーム向けとなるが「.NET TIPS:Windowsアプリケーションの多重起動を禁止するには?」で解説している。

Mainメソッド

 アプリケーションの初期化が始まる前に多重起動を禁止したい場合は、Mainメソッドの冒頭で行う必要が出てくる。しかし、WPFの既定ではMainメソッドの編集ができない。この問題については「.NET TIPS:WPF:Mainメソッドを書き変えるには?[C#/VB]」をご覧いただきたい。

 以降では、その記事に従ってMainメソッドを編集可能にしてあるものとして、話を進める。

セマフォの使い方

 多重起動の禁止に絞って説明する。

 セマフォを作るときに名前を付けると、システムグローバルなセマフォになる。

 Semaphoreクラスをインスタンス化するとき、システムにセマフォがまだ存在しないときはセマフォが新たに作られる。すでに存在していた場合は、Semaphoreクラスをインスタンス化するときにその既存のセマフォが利用される。

 従って、Semaphoreクラスをインスタンス化したときに、次のようにして多重起動かどうかを判定できる。

  • システムでセマフォが生成された:
    このプログラムは、他に起動していなかった。多重起動ではない
  • システムでセマフォが生成されなかった:
    このプログラムは、すでに起動していた。多重起動である

 なお、セマフォを使用するSemaphoreクラスのインスタンスが一つもなくなったときに、システムはセマフォを破棄する。従って、起動に成功したら、プログラム終了までSemaphoreクラスのインスタンスを保持しておく必要がある(終了時には破棄する)。

多重起動を禁止するには?

 Mainメソッドの冒頭でSemaphoreクラスのインスタンスを作り、そのときシステムで新しくセマフォが生成されたかどうかを調べればよい(次のコード)。

 Semaphoreクラスのコンストラクター引数のうち、最初の二つは同時に実行できるスレッド数を設定するものだ。多重起動を禁止するためにはそのような制御は必要ないので、適当な数でよい(ここではミューテックスと同等の動作となる「1,1」を指定した)。3番目の引数は、セマフォに付ける名前だ。アプリケーション固有の文字列となるように設定する。最後の引数は、システムで新しくセマフォが生成されたときにtrueとなる。falseだった場合は、すでにシステムにはセマフォが存在していた(=すでにプログラムが実行されていた)ということである。

[STAThread]
public static void Main()
{
  const string SemaphoreName = ".NET Tips #1120 (C#)";
  bool createdNew; 

  // Semaphoreクラスのインスタンスを生成し、アプリケーション終了まで保持する
  using (var semaphore
          = new System.Threading.Semaphore(1, 1, SemaphoreName,
                                               out createdNew))
  {
    if (!createdNew) 
    {
      // 他のプロセスが先にセマフォを作っていた
      MessageBox.Show("すでに起動しています", ".NET TIPS #1120 (C#)",
                      MessageBoxButton.OK, MessageBoxImage.Hand);
      return; // プログラム終了
    }

    // アプリケーション起動
    App app = new App();
    app.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
    app.InitializeComponent();
    app.Run();

  } // Semaphoreクラスのインスタンスを破棄
}

<STAThread> _
Public Shared Sub Main()
  Const SemaphoreName As String = ".NET Tips #1120 (VB)"
  Dim createdNew As Boolean

  ' Semaphoreクラスのインスタンスを生成し、アプリケーション終了まで保持する
  Using semaphore _
          = New System.Threading.Semaphore(1, 1, SemaphoreName,
                                           createdNew)
    If (Not createdNew) Then
      ' 他のプロセスが先にセマフォを作っていた
      MessageBox.Show("すでに起動しています", ".NET TIPS #1120 (VB)",
                      MessageBoxButton.OK, MessageBoxImage.Hand)
      Return ' プログラム終了
    End If

    ' アプリケーション起動
    Dim app As Application = New Application()
    app.StartupUri = New Uri("MainWindow.xaml", UriKind.Relative)
    app.InitializeComponent()
    app.Run()

  End Using ' Semaphoreクラスのインスタンスを破棄
End Sub

Mainメソッドに多重起動を禁止するコードを追加する(上:C#、下:VB)
太字の部分を追加した。
Visual Studio 2012で記述している。Mainメソッド自体の書き方は、「.NET TIPS:WPF:Mainメソッドを書き変えるには?[C#/VB]」を参照していただきたい。
なお、このVBのコードは、Visual Basic 2010から利用できるようになった「暗黙の行連結」を使用している。Visual Basic 2010以前の環境で試すには、適宜修正していただきたい。

 実行してみると次の画像のようになる。

2回起動したときの様子(Windows 10) 2回起動したときの様子(Windows 10)
左側が、最初に起動したウィンドウである。
そのままで2回目の起動をすると、右側のようにメッセージボックスが表示される。[OK]ボタンをクリックすると、2回目に起動した方のプログラムは終了する。
システム全体で多重起動を禁止するには、これで十分である。なお、複数のエンドユーザーがログインしている状態でも、全体で一つしか起動できない(1人のエンドユーザーしか使えない)ので注意してほしい。そのような場合に対処するには、セマフォに付ける名前にエンドユーザーの名前を付加したりする。

まとめ

 多重起動を禁止するには、ミューテックスかセマフォを使うのが一般的だ。他には、Processクラス(System.Diagnostics名前空間)を使って既存のプロセスを探す方法もある。本稿では、セマフォを使う方法を紹介した。

利用可能バージョン:.NET Framework 3.0以降
カテゴリ:WPF/XAML 処理対象:アプリケーション
使用ライブラリ:Semaphoreクラス(System.Threading名前空間)
関連TIPS:WPF:Mainメソッドを書き変えるには?[C#/VB]
関連TIPS:Windowsアプリケーションの多重起動を禁止するには?
関連TIPS:Visual Basic 2005でWindowsアプリケーションの多重起動を禁止するには?[VS 2005のみ、VB]
関連TIPS:多重起動禁止時に実行中のWindowsアプリケーションを最前面に表示するには?(Processクラスを利用)


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。