アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

WPF アプリの起動・終了処理

WPF アプリの多重起動防止や、アプリ設定の読み込みなどではまったのでメモ。

Visual Studio で WPF アプリのプロジェクトを生成すると、以下の 4 ファイルが生成される。

  1. App.xaml
  2. App.xaml.cs
  3. Window1.xaml
  4. Window1.xaml.cs

これらは XAML + コードビハインドという組み合わせになっており App がアプリケーション全体、Window1 はメイン ウィンドウを表す。WinForm 時代と異なりエントリ ポイントとなる Main メソッドは隠蔽されている。また App.xaml.cs を開いても Window1 クラスのインスタンス作成や起動処理などは書かれていない。

ではどのように Window1 を表示しているのか。App.xaml を開くと、その先頭部分に回答がある。

<Application
  x:Class="Test.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  StartupUri="Window1.xaml">

StartupUri によってアプリの起動時に自動表示されるウィンドウを指定している。アプリの多重起動チェックや前回値の読み込みなどを行う場合はこれが邪魔になるから、この記事では独自に起動処理実装することにした。

起動と終了のハンドリング

多重起動チェックやアプリ設定の読み書き処理はアプリの起動・終了時が望ましいため、これをハンドリングする。また多重起動を確認したときはウィンドウを表示せずに終了するため App.xaml から StartupUri を削除する。

<Application
  x:Class="Test.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

ついでにメインウィンドウ クラスの名前が Window1 というのは分かりにくいので MainWindow という名前に変更しておく。関連ファイルも MainWindow.xaml と MainWindowxaml.cs へ修正。

次に App.xaml.cs を開き OnStartupOnExit をオーバーライド。多重起動チェック用の Mutex もフィールドに定義しておく。

namespace Test
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        /// <summary>
        /// アプリケーションが終了する時のイベント。
        /// </summary>
        /// <param name="e">イベント データ。</param>
        protected override void OnExit(ExitEventArgs e)
        {
        }

        /// <summary>
        /// アプリケーションが開始される時のイベント。
        /// </summary>
        /// <param name="e">イベント データ。</param>
        protected override void OnStartup(StartupEventArgs e)
        {
        }

        /// <summary>
        /// 多重起動を防止する為のミューテックス。
        /// </summary>
        private static Mutex _mutex;
    }
}

このままでは起動しても何も表示されないので OnStartup に以下の実装を行う。

protected override void OnStartup(StartupEventArgs e)
{
    // 多重起動チェック
    App._mutex = new Mutex(false, "Test-{C9386A33-46F3-072b-86C4-5BF04D0A0235}");
    if (!App._mutex.WaitOne(0, false))
    {
        App._mutex.Close();
        App._mutex = null;
        this.Shutdown();
        return;
    }

    // ここでアプリケーション初期化

    // メイン ウィンドウ表示
    MainWindow window  = new MainWindow();
    window.Show();
}

Mutex コンストラクタに指定する文字列は、多重起動チェックにおいて他アプリと競合しないように GUID を指定する。重複回避としてはこれで十分だが、私は念のためにアプリ名も入れておく。Visual Studio のインストール フォルダを $ とすると $\Common7\Tools というフォルダに guidgen.exe という名前のツールがあり、これで GUID を生成できる。

Mutex を検出したときは先に起動しているアプリがあるので、すぐにプロセスを終了する。このチェックを通過したならメインウィンドウを表示する。

// メイン ウィンドウ表示
MainWindow window  = new MainWindow();
window.Show();

メインウィンドウは OnStartup 内でインスタンスを生成して表示する。インスタンスをフィールドとして保持する必要はない。

「ここでアプリケーション初期化」とコメントした箇所は MainWndow が使用するデータの設定部である。MVVM で設計して ViewModel クラスを作成しているなら、ここで関連づけておく。設定は MainWindow クラスで処理してもよいが、例えば前回の終了状態などはアプリ設定として記録する事が多いだろう。よってアプリ全体を表す App クラスで設定した方が設計的に好ましい。

起動処理が実装されたので次は終了処理を実装。オーバーライドした OnExit の中身を定義しよう。

protected override void OnExit( ExitEventArgs e )
{
    if (App._mutex == null) { return; }

    // アプリケーション設定の保存

    // ミューテックスの解放
    App._mutex.ReleaseMutex();
    App._mutex.Close();
    App._mutex = null;
}

OnStartup で多重起動と判定されたら Sutdown メソッドによりアプリを終了しているのだが、ここでも OnExit が呼び出される。そのため Mutex の null チェックを実行して余計な終了処理が走らないようにしている。

アプリ設定の保存は Settings クラスなどを使用する事になるだろう。例えば Settings.Default.Save メソッドを呼び出す。

すべての処理が完了したら Mutex を解放し、アプリ全体の終了とする。