715

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

私はそれがミューテックスと呼ばれる神話的なものと関係があることを知っています.ミューテックスが何であるかをわざわざ止めて説明する人を見つけることはめったにありません.

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

4

38 に答える 38

589

これは、Mutex ソリューションに関する非常に優れた記事です。この記事で説明されているアプローチは、2 つの理由で有利です。

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

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


アップデート

2014 年 8 月 1 日現在、上記でリンクした記事はまだ有効ですが、ブログはしばらく更新されていません。そのため、最終的にはそれが消え、それとともに提唱されている解決策がなくなるのではないかと心配しています。後世のために記事の内容をここに転載します。この言葉は、 Sanity Free Codingのブログ所有者のみに属します。

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

以前は、System.Diagnostics.Processを使用して、プロセス リストで myapp.exe のインスタンスを検索していました。これは機能しますが、多くのオーバーヘッドが発生するため、よりクリーンなものが必要でした。

これにミューテックスを使用できることを知っていたので (ただし、これまで使用したことがありませんでした)、コードを削減して生活を簡素化することに着手しました。

アプリケーション main のクラスで、Mutexという名前の static を作成しました。

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

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

Mutex.WaitOneには、待機する時間を指定するオーバーロードがあります。実際にはコードを同期させたくないので (現在使用中かどうかを確認するだけです) 、Mutex.WaitOne(Timespan timeout, bool exitContext)という 2 つのパラメーターでオーバーロードを使用します。入ることができる場合は 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 を使用して、実行中のインスタンスが既に実行中であることを誰かが忘れたことを (他のすべてのウィンドウの一番上に表示することによって) 通知することにしました。これを実現するために、PostMessageを使用してカスタム メッセージをすべてのウィンドウにブロードキャストし (カスタム メッセージは、実行中のアプリケーションによってRegisterWindowMessageに登録 されました。つまり、アプリケーションだけがそれを認識しています)、2 番目のインスタンスが終了します。実行中のアプリケーション インスタンスは、その通知を受け取り、処理します。そのために、メイン フォームでWndProcをオーバーライドし、カスタム通知をリッスンしました。その通知を受け取ったとき、フォームの TopMost プロパティを true に設定して、フォームを一番上に表示しました。

これが私が最終的に得たものです:

  • Program.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);
        }
    }
}
  • NativeMethods.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;
    }
}
于 2009-02-07T01:18:20.277 に答える
116

Mutexクラスを使用することもできますが、引数などを自分で渡すためのコードを実装する必要があることがすぐにわかります。さて、 Chris Sellの本を読んだときに、WinFormsでプログラミングするときにトリックを学びました。このトリックは、フレームワークですでに利用可能なロジックを使用します。あなたのことはわかりませんが、フレームワークで再利用できるものについて学ぶとき、それは通常、車輪の再発明の代わりに私がとるルートです。もちろん、それが私が望むすべてを行うわけではない場合を除きます。

WPFに入ると、同じコードをWPFアプリケーションで使用する方法を思いつきました。このソリューションは、質問に基づいてニーズを満たす必要があります。

まず、アプリケーションクラスを作成する必要があります。このクラスでは、OnStartupイベントをオーバーライドし、後で使用されるActivateというメソッドを作成します。

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

次に、インスタンスを管理できるクラスを作成する必要があります。その前に、Microsoft.VisualBasicアセンブリにあるコードを実際に再利用します。この例ではC#を使用しているため、アセンブリを参照する必要がありました。VB.NETを使用している場合は、何もする必要はありません。使用するクラスはWindowsFormsApplicationBaseであり、インスタンスマネージャーを継承し、プロパティとイベントを利用して単一のインスタンスを処理します。

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

基本的に、VBビットを使用して単一インスタンスを検出し、それに応じて処理します。OnStartupは、最初のインスタンスがロードされたときに発生します。OnStartupNextInstanceは、アプリケーションが再実行されたときに発生します。ご覧のとおり、イベント引数を介してコマンドラインで渡された内容を確認できます。値をインスタンスフィールドに設定しました。ここでコマンドラインを解析するか、コンストラクターとActivateメソッドの呼び出しを介してコマンドラインをアプリケーションに渡すことができます。

第三に、EntryPointを作成する時が来ました。通常のようにアプリケーションを更新する代わりに、SingleInstanceManagerを利用します。

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

さて、私はあなたがすべてに従い、この実装を使用してそれをあなた自身のものにすることができることを願っています。

于 2008-08-21T04:17:57.887 に答える
90

ここから。

クロスプロセス Mutex の一般的な用途は、一度にプログラムのインスタンスのみを実行できるようにすることです。方法は次のとおりです。

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

Mutex の優れた機能は、ReleaseMutex が最初に呼び出されずにアプリケーションが終了した場合、CLR が Mutex を自動的に解放することです。

于 2008-08-21T00:47:28.983 に答える
67

MSDNには、実際にこれを正確に行うための C# と VB の両方のサンプル アプリケーションがあります。

単一インスタンス検出を開発するための最も一般的で信頼性の高い手法は、Microsoft .NET Framework リモート処理インフラストラクチャ (System.Remoting) を使用することです。Microsoft .NET Framework (バージョン 2.0) には、必要なリモート機能をカプセル化する WindowsFormsApplicationBase 型が含まれています。この型を WPF アプリケーションに組み込むには、型を派生させ、アプリケーションの静的エントリ ポイント メソッド Main と WPF アプリケーションの Application 型の間のシムとして使用する必要があります。shim は、アプリケーションが最初に起動されたときと、その後の起動が試行されたときを検出し、yield によって WPF アプリケーション タイプを制御して、起動の処理方法を決定します。

  • C# の場合、深呼吸をして、「VisualBasic DLL を含めたくない」ということを忘れてしまいます。このことと、Scott Hanselman が言っていることと、これが問題に対する最もクリーンな解決策であり、フレームワークについてあなたよりも多くのことを知っている人々によって設計されているという事実のためです。
  • 使いやすさの観点から言えば、ユーザーがアプリケーションをロードしていて、それが既に開いていて、次のようなエラー メッセージを表示している場合、ユーザーは'Another instance of the app is running. Bye'あまり満足していません。(GUI アプリケーションで) そのアプリケーションに切り替えて、提供された引数を渡す必要があります。または、コマンド ライン パラメーターに意味がない場合は、最小化されている可能性のあるアプリケーションをポップアップする必要があります。

フレームワークはすでにこれをサポートしています - 馬鹿がDLLに名前を付けただけで、DLLMicrosoft.VisualBasicに入れられなかったMicrosoft.ApplicationUtilsか、そのようなものです。それを乗り越えるか、リフレクターを開きます。

ヒント: このアプローチをそのまま使用し、リソースなどを含む App.xaml が既にある場合は、これも参照してください

于 2010-09-27T21:48:24.097 に答える
25

このコードはメイン メソッドに移動する必要があります。WPF の main メソッドの詳細については、こちらを参照してください。

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

private const int SW_SHOWMAXIMIZED = 3;

static void Main() 
{
    Process currentProcess = Process.GetCurrentProcess();
    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();
    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
       return; 
    }
}

方法 2

static void Main()
{
    string procName = Process.GetCurrentProcess().ProcessName;
    // get the list of all processes by that name

    Process[] processes=Process.GetProcessesByName(procName);

    if (processes.Length > 1)
    {
        MessageBox.Show(procName + " already running");  
        return;
    } 
    else
    {
        // Application.Run(...);
    }
}

注:上記の方法は、プロセス/アプリケーションに一意の名前があることを前提としています。プロセス名を使用して、既存のプロセッサがあるかどうかを確認するためです。そのため、アプリケーションに非常に一般的な名前 (メモ帳など) が付いている場合、上記の方法は機能しません。

于 2011-08-24T22:48:30.580 に答える
20

さて、私はこれのために、ほとんどのユースケースで簡単に機能する使い捨てのクラスを持っています:

次のように使用します。

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

ここにあります:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}
于 2010-02-19T10:39:42.000 に答える
16

Mutex と IPC を使用し、コマンドライン引数を実行中のインスタンスに渡す新しいものは、WPF Single Instance Applicationです。

于 2010-05-28T19:58:11.210 に答える
13

マークされた回答のリファレンスであるコードC# .NET Single Instance Applicationは素晴らしいスタートです。

ただし、既に存在するインスタンスでモーダル ダイアログが開いている場合、そのダイアログが管理対象 (about ボックスなどの別のフォームなど) であるか、非管理対象 (標準の .NET クラスを使用している場合でも OpenFileDialog を使用できます)。元のコードでは、メイン フォームはアクティブ化されますが、モーダル フォームは非アクティブなままで、奇妙に見えます。さらに、ユーザーはアプリを使い続けるためにクリックする必要があります。

そのため、SingleInstance ユーティリティ クラスを作成して、Winforms および WPF アプリケーションでこれらすべてを自動的に処理しました。

Winforms :

1) Program クラスを次のように変更します。

static class Program
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);

    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

2) メイン ウィンドウ クラスを次のように変更します。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        // if needed, the singleton will restore this window
        Program.Singleton.OnWndProc(this, m, true);

        // TODO: handle specific messages here if needed
        base.WndProc(ref m);
    }
}

WPF:

1) アプリ ページを次のように変更します (メイン メソッドを再定義できるように、そのビルド アクションを page に設定してください)。

public partial class App : Application
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);

    [STAThread]
    public static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }
}

2) メイン ウィンドウ クラスを次のように変更します。

public partial class MainWindow : Window
{
    private HwndSource _source;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        _source = (HwndSource)PresentationSource.FromVisual(this);
        _source.AddHook(HwndSourceHook);
    }

    protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // if needed, the singleton will restore this window
        App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);

        // TODO: handle other specific message
        return IntPtr.Zero;
    }

そして、ここにユーティリティクラスがあります:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceUtilities
{
    public sealed class SingleInstance
    {
        private const int HWND_BROADCAST = 0xFFFF;

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int RegisterWindowMessage(string message);

        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        public SingleInstance(string uniqueName)
        {
            if (uniqueName == null)
                throw new ArgumentNullException("uniqueName");

            Mutex = new Mutex(true, uniqueName);
            Message = RegisterWindowMessage("WM_" + uniqueName);
        }

        public Mutex Mutex { get; private set; }
        public int Message { get; private set; }

        public void RunFirstInstance(Action action)
        {
            RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
        }

        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (WaitForMutext(wParam, lParam))
            {
                try
                {
                    action();
                }
                finally
                {
                    ReleaseMutex();
                }
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return;

            FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
        }

        public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
        {
            if (m == Message)
            {
                if (restorePlacement)
                {
                    WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
                    if (placement.IsValid && placement.IsMinimized)
                    {
                        const int SW_SHOWNORMAL = 1;
                        placement.ShowCmd = SW_SHOWNORMAL;
                        placement.SetPlacement(hwnd);
                    }
                }

                if (activate)
                {
                    SetForegroundWindow(hwnd);
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
                }
            }
        }

#if WINFORMS // define this for Winforms apps
        public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            if (m == Message)
            {
                if (activate)
                {
                    if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                    {
                        form.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    }

                    form.Activate();
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
                }
            }
        }

        public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
        }
#endif

        public void ReleaseMutex()
        {
            Mutex.ReleaseMutex();
        }

        public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
        {
            bool b = PrivateWaitForMutext(force);
            if (!b)
            {
                PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
            }
            return b;
        }

        public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
        {
            return WaitForMutext(false, wParam, lParam);
        }

        private bool PrivateWaitForMutext(bool force)
        {
            if (force)
                return true;

            try
            {
                return Mutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }

    // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowPlacement
    {
        public int Length { get; set; }
        public int Flags { get; set; }
        public int ShowCmd { get; set; }
        public int MinPositionX { get; set; }
        public int MinPositionY { get; set; }
        public int MaxPositionX { get; set; }
        public int MaxPositionY { get; set; }
        public int NormalPositionLeft { get; set; }
        public int NormalPositionTop { get; set; }
        public int NormalPositionRight { get; set; }
        public int NormalPositionBottom { get; set; }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        private const int SW_SHOWMINIMIZED = 2;

        public bool IsMinimized
        {
            get
            {
                return ShowCmd == SW_SHOWMINIMIZED;
            }
        }

        public bool IsValid
        {
            get
            {
                return Length == Marshal.SizeOf(typeof(WindowPlacement));
            }
        }

        public void SetPlacement(IntPtr windowHandle)
        {
            SetWindowPlacement(windowHandle, ref this);
        }

        public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
        {
            WindowPlacement placement = new WindowPlacement();
            if (windowHandle == IntPtr.Zero)
                return placement;

            placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
            if (!GetWindowPlacement(windowHandle, ref placement))
            {
                if (throwOnError)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return new WindowPlacement();
            }
            return placement;
        }
    }

    public static class FormUtilities
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);

        private class ModalWindowUtil
        {
            private const int GW_OWNER = 4;
            private int _maxOwnershipLevel;
            private IntPtr _maxOwnershipHandle;

            private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
            {
                int level = 1;
                if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
                {
                    if (level > _maxOwnershipLevel)
                    {
                        _maxOwnershipHandle = hwnd;
                        _maxOwnershipLevel = level;
                    }
                }
                return true;
            }

            private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
            {
                IntPtr o = GetWindow(hwnd, GW_OWNER);
                if (o == IntPtr.Zero)
                    return false;

                if (o == owner)
                    return true;

                level++;
                return IsOwned(owner, o, ref level);
            }

            public static void ActivateWindow(IntPtr hwnd)
            {
                if (hwnd != IntPtr.Zero)
                {
                    SetActiveWindow(hwnd);
                }
            }

            public static IntPtr GetModalWindow(IntPtr owner)
            {
                ModalWindowUtil util = new ModalWindowUtil();
                EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
                return util._maxOwnershipHandle; // may be IntPtr.Zero
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            ModalWindowUtil.ActivateWindow(hwnd);
        }

        public static IntPtr GetModalWindow(IntPtr owner)
        {
            return ModalWindowUtil.GetModalWindow(owner);
        }
    }
}
于 2013-05-13T12:40:03.607 に答える
10

いくつかの考え: アプリケーションの 1 つのインスタンスだけが「不自由」ではないことを要求する場合があります。データベース アプリなどは、1 人のユーザーがデータベースにアクセスするアプリの複数のインスタンスを許可する場合、桁違いに難しくなります (ユーザーのアプリの複数のインスタンスで開かれているすべてのレコードを更新するすべてのことを知っています)。マシンなど)。まず、「名前の衝突」については、人間が読める名前を使用しないでください。代わりに GUID を使用するか、GUID + 人間が読める名前を使用することをお勧めします。名前の衝突の可能性はレーダーから外れただけで、Mutex は気にしません。 . 誰かが指摘したように、DOS 攻撃は最悪ですが、悪意のある人物がミューテックス名を取得し、それを自分のアプリに組み込むのに苦労した場合、とにかく、あなたはほとんどターゲットであり、ミューテックス名をいじるだけでなく、自分自身を保護するためにもっと多くのことをしなければなりません. また、次のバリアントを使用する場合: new Mutex(true, "some GUID plus Name", out AIsFirstInstance) 、Mutex が最初のインスタンスであるかどうかに関するインジケーターが既にあります。

于 2009-12-17T21:25:51.297 に答える
8

次のコードは、単一インスタンス アプリケーションを登録するための WCF 名前付きパイプ ソリューションです。別のインスタンスが起動しようとしたときにもイベントを発生させ、別のインスタンスのコマンド ラインを受け取るので、これは素晴らしいことです。

クラスを使用するため、WPF 向けですSystem.Windows.StartupEventHandlerが、これは簡単に変更できます。

このコードにはPresentationFramework、 およびへの参照が必要System.ServiceModelです。

使用法:

class Program
{
    static void Main()
    {
        var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");

        if (SingleInstanceManager.VerifySingleInstance(applicationId))
        {
            SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;

            // Start the application
        }
    }

    static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
    {
        // Do something in response to another instance starting up.
    }
}

ソースコード:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
  /// <summary>
  /// Raised when another instance attempts to start up.
  /// </summary>
  public static event StartupEventHandler OtherInstanceStarted;

  /// <summary>
  /// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
  /// send the main instance this instance's startup information.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if this instance is the main instance.</returns>
  public static bool VerifySingleInstace(Guid guid)
  {
    if (!AttemptPublishService(guid))
    {
      NotifyMainInstance(guid);

      return false;
    }

    return true;
  }

  /// <summary>
  /// Attempts to publish the service.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if the service was published successfully.</returns>
  private static bool AttemptPublishService(Guid guid)
  {
    try
    {
      ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
      NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
      serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
      serviceHost.Open();

      return true;
    }
    catch
    {
      return false;
    }
  }

  /// <summary>
  /// Notifies the main instance that this instance is attempting to start up.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  private static void NotifyMainInstance(Guid guid)
  {
    NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
    using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
    {
      ISingleInstance singleInstance = factory.CreateChannel();
      singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
    }
  }

  /// <summary>
  /// Creates an address to publish/contact the service at based on a globally unique identifier.
  /// </summary>
  /// <param name="guid">The identifier for the application.</param>
  /// <returns>The address to publish/contact the service.</returns>
  private static string CreateAddress(Guid guid)
  {
    return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
  }

  /// <summary>
  /// The interface that describes the single instance service.
  /// </summary>
  [ServiceContract]
  private interface ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    [OperationContract]
    void NotifyMainInstance(string[] args);
  }

  /// <summary>
  /// The implementation of the single instance service interface.
  /// </summary>
  private class SingleInstance : ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    public void NotifyMainInstance(string[] args)
    {
      if (OtherInstanceStarted != null)
      {
        Type type = typeof(StartupEventArgs);
        ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
        StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
        FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
        Debug.Assert(argsField != null);
        argsField.SetValue(e, args);

        OtherInstanceStarted(null, e);
      }
    }
  }
}
于 2013-04-19T05:38:40.217 に答える
6

そのような一見単​​純な質問に対する非常に多くの答え。ここで少し物事を揺るがすだけで、この問題に対する私の解決策です。

ミューテックスの作成は、JIT-erがコードのごく一部でのみ使用していることを確認し、ガベージコレクションの準備ができていることをマークしたいため、面倒な場合があります。そのMutexをそれほど長く使用しないと思っているので、かなり賢くなりたいと思っています。実際には、アプリケーションが実行されている限り、このMutexを使い続ける必要があります。ガベージコレクターにMutexをそのままにしておくように指示する最良の方法は、さまざまな世代のガベージコレクションを通じてガベージコレクターを存続させるように指示することです。例:

var m = new Mutex(...);
...
GC.KeepAlive(m);

私はこのページからアイデアを持ち上げました:http://www.ai.uga.edu/~mc/SingleInstance.html

于 2010-05-05T00:54:24.510 に答える
6

これを処理するための本当に良い方法があるようです:

WPF 単一インスタンス アプリケーション

これにより、すべてのミューテックスとメッセージングの欠片を管理する追加可能なクラスが提供され、実装が簡単になるまで簡素化されます。

于 2011-08-11T17:11:57.043 に答える
5

次のコードを見てください。これは、WPFアプリケーションの複数のインスタンスを防ぐための優れたシンプルなソリューションです。

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process thisProc = Process.GetCurrentProcess();
    if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
    {
        MessageBox.Show("Application running");
        Application.Current.Shutdown();
        return;
    }

    var wLogin = new LoginWindow();

    if (wLogin.ShowDialog() == true)
    {
        var wMain = new Main();
        wMain.WindowState = WindowState.Maximized;
        wMain.Show();
    }
    else
    {
        Application.Current.Shutdown();
    }
}
于 2012-11-30T13:37:02.957 に答える
4

単一インスタンスアプリケーションを実装するために(または少なくとも本番コードでは)名前付きミューテックスを使用しないでください。悪意のあるコードは、お尻を簡単にDoS(サービス拒否)する可能性があります...

于 2009-01-26T20:27:14.963 に答える
4

2017-01-25 を更新。いくつかのことを試した後、VisualBasic.dll を使用することにしました。VisualBasic.dll の方が簡単で、(少なくとも私にとっては) うまく機能します。以前の回答を参考にさせていただきます...

参考までに、これは引数を渡さずに行った方法です(そうする理由が見つかりません...あるインスタンスから別のインスタンスに渡される引数を持つ単一のアプリを意味します)。ファイルの関連付けが必要な場合は、(ユーザーの標準的な期待に従って) アプリをドキュメントごとにインスタンス化する必要があります。引数を既存のアプリに渡す必要がある場合は、vb dll を使用すると思います。

引数を渡さない (単一インスタンス アプリのみ) ため、Matt Davis ソリューションで定義されているように、新しいウィンドウ メッセージを登録せず、メッセージ ループをオーバーライドしないことを好みます。VisualBasic dll を追加することは大したことではありませんが、単一インスタンス アプリを実行するためだけに新しい参照を追加することはお勧めしません。また、App.Startup オーバーライドから Shutdown を呼び出す代わりに、Main を使用して新しいクラスをインスタンス化して、できるだけ早く終了できるようにすることを好みます。

誰かがそれを気に入ってくれることを願っています...または少しインスピレーションを与えるでしょう:-)

プロジェクトのスタートアップ クラスは「SingleInstanceApp」として設定する必要があります。

public class SingleInstanceApp
{
    [STAThread]
    public static void Main(string[] args)
    {
        Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");

        if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

            }
            finally
            {
                _mutexSingleInstance.ReleaseMutex();
                _mutexSingleInstance.Close();
            }
        }
        else
        {
            MessageBox.Show("One instance is already running.");

            var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
            {
                if (processes.Length > 1)
                {
                    foreach (var process in processes)
                    {
                        if (process.Id != Process.GetCurrentProcess().Id)
                        {
                            WindowHelper.SetForegroundWindow(process.MainWindowHandle);
                        }
                    }
                }
            }
        }
    }
}

ウィンドウヘルパー:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace HQ.Util.Unmanaged
{
    public class WindowHelper
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
于 2013-12-05T23:00:54.160 に答える
4

これが私が使用するものです。「アクティブなクリッカー」から保護するために、切り替えとミューテックスを実行するプロセス列挙を組み合わせました。

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }
于 2010-06-22T00:04:21.567 に答える
4

Dale Ragan のものに似た、より単純な解決策を見つけましたが、わずかに変更されました。標準の Microsoft WindowsFormsApplicationBase クラスに基づいて、実質的に必要なすべてのことを行います。

まず、Windows フォームを使用する他のすべての単一インスタンス アプリケーションで使用できる SingleInstanceController クラスを作成します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace SingleInstanceController_NET
{
    public class SingleInstanceController
    : WindowsFormsApplicationBase
    {
        public delegate Form CreateMainForm();
        public delegate void StartNextInstanceDelegate(Form mainWindow);
        CreateMainForm formCreation;
        StartNextInstanceDelegate onStartNextInstance;
        public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
        {
            // Set whether the application is single instance
            this.formCreation = formCreation;
            this.onStartNextInstance = onStartNextInstance;
            this.IsSingleInstance = true;

            this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);                      
        }

        void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
        {
            if (onStartNextInstance != null)
            {
                onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
                                                    // for example, by clicking on the exe file.
            }                                       // This code can determine how to re-activate the existing main window of the running application.
        }

        protected override void OnCreateMainForm()
        {
            // Instantiate your main application form
            this.MainForm = formCreation();
        }

        public void Run()
        {
            string[] commandLine = new string[0];
            base.Run(commandLine);
        }
    }
}

次に、次のようにプログラムで使用できます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;

namespace SingleInstance
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static Form CreateForm()
        {
            return new Form1(); // Form1 is used for the main window.
        }

        static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
                                                         // the main window is activated again.
        {
            mainWindow.WindowState = FormWindowState.Maximized;
        }
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);            
            SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
            controller.Run();         
        }
    }
}

プログラムと SingleInstanceController_NET ソリューションの両方が Microsoft.VisualBasic を参照する必要があります。ユーザーが実行中のプログラムを再起動しようとしたときに、実行中のアプリケーションを通常のウィンドウとして再アクティブ化するだけの場合は、SingleInstanceController の 2 番目のパラメーターを null にすることができます。この例では、ウィンドウが最大化されています。

于 2011-04-04T09:06:08.060 に答える
3

ただし、Mutex は使用しません。簡単な答えは次のとおりです。

System.Diagnostics;    
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;

if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

の中に入れProgram.Main()ます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sample
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //simple add Diagnostics namespace, and these 3 lines below 
            string thisprocessname = Process.GetCurrentProcess().ProcessName;
            if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Sample());
        }
    }
}

-ステートメントに追加MessageBox.Showして、if「アプリケーションはすでに実行中です」と入力できます。
これは誰かに役立つかもしれません。

于 2016-12-30T04:30:21.317 に答える
2

NativeMethods クラスに sendMessage メソッドを追加しました。

アプリケーションがタスクバーに表示されていない場合、postmessage メソッドは明らかに機能しませんが、sendmessage メソッドを使用するとこれが解決します。

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.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
于 2015-01-26T08:35:05.830 に答える
1

これは、Event を介して実装されたものと同じです。

public enum ApplicationSingleInstanceMode
{
    CurrentUserSession,
    AllSessionsOfCurrentUser,
    Pc
}

public class ApplicationSingleInstancePerUser: IDisposable
{
    private readonly EventWaitHandle _event;

    /// <summary>
    /// Shows if the current instance of ghost is the first
    /// </summary>
    public bool FirstInstance { get; private set; }

    /// <summary>
    /// Initializes 
    /// </summary>
    /// <param name="applicationName">The application name</param>
    /// <param name="mode">The single mode</param>
    public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
    {
        string name;
        if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
            name = $"Local\\{applicationName}";
        else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
            name = $"Global\\{applicationName}{Environment.UserDomainName}";
        else
            name = $"Global\\{applicationName}";

        try
        {
            bool created;
            _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
            FirstInstance = created;
        }
        catch
        {
        }
    }

    public void Dispose()
    {
        _event.Dispose();
    }
}
于 2016-07-11T11:56:17.780 に答える
1

これが私がこの問題を処理することになった方法です。テスト用のデバッグ コードがまだ残っていることに注意してください。このコードは、App.xaml.cs ファイルの OnStartup 内にあります。(WPF)

        // Process already running ? 
        if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
        {

            // Show your error message
            MessageBox.Show("xxx is already running.  \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);

            // This process 
            Process currentProcess = Process.GetCurrentProcess();

            // Get all processes running on the local computer.
            Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

            // ID of this process... 
            int temp = currentProcess.Id;
            MessageBox.Show("This Process ID:  " + temp.ToString());

            for (int i = 0; i < localAll.Length; i++)
            {
                // Find the other process 
                if (localAll[i].Id != currentProcess.Id)
                {
                    MessageBox.Show("Original Process ID (Switching to):  " + localAll[i].Id.ToString());

                    // Switch to it... 
                    SetForegroundWindow(localAll[i].MainWindowHandle);

                }
            }

            Application.Current.Shutdown();

        }

これには、まだ把握していない問題がある可能性があります。何かに遭遇した場合は、回答を更新します。

于 2016-08-09T14:35:08.073 に答える
0

通常、これは私が単一インスタンスのWindowsフォームアプリケーションに使用するコードです。

[STAThread]
public static void Main()
{
    String assemblyName = Assembly.GetExecutingAssembly().GetName().Name;

    using (Mutex mutex = new Mutex(false, assemblyName))
    {
        if (!mutex.WaitOne(0, false))
        {
            Boolean shownProcess = false;
            Process currentProcess = Process.GetCurrentProcess();

            foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
            {
                if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero))
                {
                    IntPtr windowHandle = process.MainWindowHandle;

                    if (NativeMethods.IsIconic(windowHandle))
                        NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore);

                    NativeMethods.SetForegroundWindow(windowHandle);

                    shownProcess = true;
                }
            }

            if (!shownProcess)
                MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form());
        }
    }
}

ネイティブコンポーネントは次のとおりです。

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean IsIconic([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command);

public enum ShowWindowCommand : int
{
    Hide                   = 0x0,
    ShowNormal             = 0x1,
    ShowMinimized          = 0x2,
    ShowMaximized          = 0x3,
    ShowNormalNotActive    = 0x4,
    Minimize               = 0x6,
    ShowMinimizedNotActive = 0x7,
    ShowCurrentNotActive   = 0x8,
    Restore                = 0x9,
    ShowDefault            = 0xA,
    ForceMinimize          = 0xB
}
于 2013-01-15T12:20:54.640 に答える
0

exeが他のパスから呼び出された場合、複数のインスタンスを許可するソリューションが好きです。CharithJ ソリューションを修正しました メソッド 1:

   static class Program {
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
    [DllImport("User32.dll")]
    public static extern Int32 SetForegroundWindow(IntPtr hWnd);
    [STAThread]
    static void Main() {
        Process currentProcess = Process.GetCurrentProcess();
        foreach (var process in Process.GetProcesses()) {
            try {
                if ((process.Id != currentProcess.Id) && 
                    (process.ProcessName == currentProcess.ProcessName) &&
                    (process.MainModule.FileName == currentProcess.MainModule.FileName)) {
                    ShowWindow(process.MainWindowHandle, 5); // const int SW_SHOW = 5; //Activates the window and displays it in its current size and position. 
                    SetForegroundWindow(process.MainWindowHandle);
                    return;
                }
            } catch (Exception ex) {
                //ignore Exception "Access denied "
            }
        }

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}
于 2016-09-29T16:51:23.127 に答える
0

これが私の2セントです

 static class Program
    {
        [STAThread]
        static void Main()
        {
            bool createdNew;
            using (new Mutex(true, "MyApp", out createdNew))
            {
                if (createdNew) {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    var mainClass = new SynGesturesLogic();
                    Application.ApplicationExit += mainClass.tray_exit;
                    Application.Run();
                }
                else
                {
                    var current = Process.GetCurrentProcess();
                    foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
                    {
                        NativeMethods.SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
        }
    }
于 2016-08-26T07:11:53.400 に答える
-1

Matt Davis の回答に基づいており、利便性のためにクラスにラップされています。

public static class SingleAppInstanceChecker
{
    /// <summary>
    /// Arbitrary unique string
    /// </summary>
    private static Mutex _mutex = new Mutex(true, "0d12ad74-026f-40c3-bdae-e178ddee8602");

    public static bool IsNotRunning()
    {
        return _mutex.WaitOne(TimeSpan.Zero, true);
    }
}

使用例:

private void Application_Startup(object sender, StartupEventArgs e)
{
    if (!SingleAppInstanceChecker.IsNotRunning())
    {
        MessageBox.Show("Application is already running.");

        // Exit application using:
        // Environment.Exit(1);
        // Application.Current.Shutdown();
        // Etc...

        return;
    }
    
    // Allow startup and continue with normal processing
    // ...
}
于 2021-07-01T17:59:47.173 に答える