redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

2重起動防止処理を組み込んだWPFアプリでClickOnceの更新と再起動を行う

タイトルでやりたい事を伝えきったので、やり方を説明します。

ClickOnceの更新方法は、探せば見つかると思いますので割愛します。

参考:ClickOnce に関するまとめ - Qiita

ClickOnceによる更新は、アプリケーションを再起動しないと行われません。
なので、ClickOnceの更新メソッドを呼び出した後に、以下のコードを実行して、アプリケーションを再起動します。

System.Windows.Forms.Application.Restart();
System.Windows.Application.Current.Shutdown();

一方、WPFアプリケーションで2重起動を防止したい時は、App.xaml.csを編集し、Mutexを使用して以下のように記述すると思います。

参考:WPFでアプリの2重起動を禁止する。 - プログラムを書こう!

private static readonly Mutex Mutex = new Mutex(false, "Mutex名");
private static bool _hasHandle = false;

private void OnStartup(object sender, StartupEventArgs e)
{
    _hasHandle = Mutex.WaitOne(0, false);

    if (!_hasHandle)
    {
        MessageBox.Show("2重起動はできません。");

        Shutdown();
        return;
    }
}

private void OnExit(object sender, ExitEventArgs e)
{
    if (_hasHandle)
    {
        Mutex.ReleaseMutex();
    }

    Mutex.Close();
}

ここで終了確認をしようと思って、終了プロセスのどこかで、MessageBox.Showによるメッセージ表示を組み込んだとします。

その状態で、ClickOnceの更新+アプリケーションの再起動をすると、終了確認のメッセージに加えて、なんと「2重起動はできません。」というメッセージが表示されてしまいました。

これは終了処理が「終了確認のメッセージを表示する」ところで止まったため、Mutexの解放をする前に新しいアプリケーションを起動しようとして、2重起動防止処理が動いてしまった結果です。

パラメータ等による分岐で、更新時は終了確認のメッセージ表示をやめることで、とりあえず動作するようにはなりました。

しかし、終了処理に時間がかかるようになれば、Mutexの解放をする前に新しいアプリケーションが起動する可能性が残っています。

これを解消するために、最終的にはアプリケーションの起動処理をMutexの解放後にもっていきました。

ViewModel

IsRestart = true;        //ViewModelでの終了処理の前に、再起動フラグを設定する
Application.Current.Shutdown();

App.xaml.cs

private void OnExit(object sender, ExitEventArgs e)
{
    // Mutexの所有権を保持しているプロセスが実行する
    if (_hasHandle)
    {
        // Mutexを解放します
        Mutex.ReleaseMutex();
    }

    // Mutexのクローズ処理
    // 両方のプロセスで実行される
    Mutex.Close();

    // Mutexの解放後に、新しいアプリケーションを起動する
    var viewModel = (MainWindowViewModel) _mainWindow.DataContext;
    if (viewModel.IsRestart)
    {
        // 新しいアプリケーションの起動(このメソッドでは、WPFアプリケーションは終了しない)
        System.Windows.Forms.Application.Restart();
    }
}

これによって、2重起動防止処理を組み込んだ状態で、ClickOnceの更新と再起動を行うことが出来ました。

以上。