単一インスタンスの WPF アプリケーションを作成する正しい方法は何ですか? 質問する

単一インスタンスの WPF アプリケーションを作成する正しい方法は何ですか? 質問する

.NETでC#とWPFを使用する(Windows フォームまたはコンソール) の場合、単一のインスタンスとしてのみ実行できるアプリケーションを作成する正しい方法は何ですか?

これはミューテックスと呼ばれる神話上のものと関係があることはわかっていますが、わざわざ立ち止まってミューテックスとは何かを説明してくれる人はほとんどいません。

コードは、すでに実行中のインスタンスに、ユーザーが 2 番目のインスタンスを起動しようとしたことを通知する必要があり、コマンドライン引数が存在する場合はそれも渡す必要があります。

ベストアンサー1

これはとても良い記事Mutex ソリューションに関して。この記事で説明されているアプローチには、2 つの理由から利点があります。

まず、Microsoft.VisualBasicアセンブリへの依存を必要としません。プロジェクトが既にそのアセンブリに依存していた場合、私はおそらくこのアプローチの使用を推奨します。別の回答に示されているしかし、現状では、Microsoft.VisualBasic アセンブリは使用しておらず、プロジェクトに不要な依存関係を追加したくありません。

次に、この記事では、ユーザーが別のインスタンスを起動しようとしたときに、アプリケーションの既存のインスタンスをフォアグラウンドにする方法を説明しています。これは、ここで説明されている他の Mutex ソリューションでは対応されていない、非常に優れた機能です。


アップデート

2014年8月1日現在、私が上でリンクした記事はまだアクティブですが、ブログはしばらく更新されていません。そのため、最終的にはブログが消えてしまうのではないかと心配しています。それに伴い、提案された解決策も消えてしまいます。後世のために、この記事の内容をここに転載します。言葉はブログ所有者にのみ帰属します。健全なコーディング

今日は、アプリケーションが自身の複数のインスタンスを実行することを禁止するコードをリファクタリングしたいと考えました。

以前は使用していたシステム.診断.プロセスプロセス リストで myapp.exe のインスタンスを検索します。これは機能しますが、オーバーヘッドがかなり発生するため、もっとクリーンなものが欲しかったのです。

これにはミューテックスを使用できることはわかっていましたが (これまで使用したことがありませんでした)、コードを削減して作業を簡素化することにしました。

私のアプリケーションのメインクラスに、静的な名前のクラスを作成しました。ミューテックス:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

名前付きミューテックスを使用すると、複数のスレッドとプロセスにわたって同期をスタックできます。これはまさに私が探していた魔法です。

ミューテックス.WaitOne待機する時間を指定するオーバーロードがあります。実際にコードを同期するわけではないので (現在使用中かどうかをチェックするだけ)、2 つのパラメータを持つオーバーロードを使用します。Mutex.WaitOne(タイムスパンタイムアウト、bool exitContext). 待機 1 は、入ることができる場合は true を返し、入ることができなかった場合は false を返します。この場合、待機はまったく必要ありません。ミューテックスが使用されている場合は、それをスキップして先に進みます。そのため、TimeSpan.Zero (0 ミリ秒待機) を渡し、exitContext を true に設定して、ロックを取得する前に同期コンテキストを終了できるようにします。これを使用して、Application.Run コードを次のようなコード内にラップします。

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

したがって、アプリが実行中の場合、WaitOne は false を返し、メッセージ ボックスが表示されます。

メッセージボックスを表示する代わりに、私は小さなWin32を利用して、実行中のインスタンスに、誰かがすでに実行中であることを忘れていることを通知することにしました(他のすべてのウィンドウの一番上に表示することで)。これを実現するために、私は投稿メッセージすべてのウィンドウにカスタムメッセージをブロードキャストします(カスタムメッセージはウィンドウメッセージの登録実行中のアプリケーションによって通知され、つまりアプリケーションだけがそれが何であるかを知っている)その後、2番目のインスタンスが終了します。実行中のアプリケーションインスタンスはその通知を受信して​​処理します。そのために、ウィンドウプロシージャメイン フォームで、カスタム通知をリッスンしました。通知を受け取ったら、フォームの TopMost プロパティを true に設定して、通知を一番上に表示しました。

最終的にできたのは次の通りです:

  • プログラム.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • ネイティブメソッド.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs(前面部分)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}

おすすめ記事