创建单实例WPF应用程序的正确方法是什么?

时间:2008-08-21 00:33:59

标签: c# .net wpf mutex

在.NET下使用C#和WPF(而不是Windows Forms或控制台),创建只能作为单个实例运行的应用程序的正确方法是什么?

我知道它与一些叫做互斥的神秘事物有关,我很少能找到一个不愿意停下来解释其中一个是什么的人。

代码还需要告知已经运行的实例用户尝试启动第二个实例,并且如果存在任何命令行参数,也可以传递它。

39 个答案:

答案 0 :(得分:488)

关于Mutex解决方案,这是一个非常好的article。由于两个原因,该文章描述的方法是有利的。

首先,它不需要依赖于Microsoft.VisualBasic程序集。如果我的项目已经依赖于该程序集,我可能会主张使用方法shown in another answer。但实际上,我没有使用Microsoft.VisualBasic程序集,我宁愿不为我的项目添加不必要的依赖项。

其次,本文展示了当用户尝试启动另一个实例时如何将现有应用程序实例置于前台。这是一个非常好的触摸,这里描述的其他Mutex解决方案没有解决。


更新

截至2014年8月1日,我上面链接的文章仍然有效,但博客暂时没有更新。这让我担心最终它可能会消失,并且随之而来的是提倡的解决方案。我正在为这个后代再现这篇文章的内容。这些字词仅属于Sanity Free Coding的博客所有者。

  

今天我想重构一些禁止我申请的代码   从运行自己的多个实例。

     

以前我曾使用System.Diagnostics.Process来搜索   进程列表中myapp.exe的实例。虽然这有效,但它   带来了很多开销,我想要更清洁的东西。

     

知道我可以使用互斥锁(但从未使用过它)   之前)我开始减少我的代码并简化我的生活。

     

在我的应用程序主类中,我创建了一个名为Mutex的静态:

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

拥有一个命名的互斥锁允许我们跨越同步堆栈   多个线程和进程,这正是我正在寻找的魔力   对

     

Mutex.WaitOne有一个重载,为我们指定了一段时间   等待。因为我们实际上并不想同步我们的代码   (更多只是检查它是否正在使用中)我们使用重载   两个参数:Mutex.WaitOne(Timespan timeout, bool exitContext)。   等待一个如果能够进入则返回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注册   由我运行的应用程序,这意味着只有我的应用程序知道什么   它是)然后我的第二个实例退出。运行的应用程序实例   会收到该通知并处理它。为了做到这一点,我   在我的主表单中覆盖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;
    }
}

答案 1 :(得分:100)

你可以使用Mutex类,但很快就会发现你需要实现代码来传递参数等等。好吧,当我阅读Chris Sell's book时,我在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);
    }
}

好吧,我希望你能够遵循一切,并能够使用这个实现并使其成为你自己的。

答案 2 :(得分:81)

来自here

跨进程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。

答案 3 :(得分:55)

MSDN实际上有一个C#和VB的示例应用程序来完成这个:http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

  

最常见,最可靠的技术   用于开发单实例   检测是使用Microsoft .NET   框架远程基础设施   (System.Remoting)。 Microsoft .NET   框架(2.0版)包括一个   type,WindowsFormsApplicationBase,   它封装了所需的   远程功能。合并   将此类型转换为WPF应用程序,a   类型需要从中派生出来,并且是   用作应用程序之间的垫片   静态入口点方法,Main,和   WPF应用程序的应用程序   类型。垫片检测到何时   应用程序首次启动,并且   当后续发布时   尝试,并且yield控制WPF   应用程序类型确定如何   处理发布会。

  • 对于C#,人们只需深呼吸,忘记整个'我不想包含VisualBasic DLL'。由于thisScott Hanselman says以及这几乎是解决这个问题最简洁的解决方案的事实,并且由那些比你更了解框架的人设计。
  • 从可用性的角度来看,事实是如果您的用户正在加载一个应用程序并且它已经打开并且您正在给他们一个错误消息,例如'Another instance of the app is running. Bye'那么他们就不会是一个非常开心的用户。您只需(在GUI应用程序中)切换到该应用程序并传入提供的参数 - 或者如果命令行参数没有意义,则必须弹出可能已最小化的应用程序。

框架已经支持这个 - 只是有些白痴命名为DLL Microsoft.VisualBasic并且它没有被放入Microsoft.ApplicationUtils或类似的东西。克服它 - 或打开反射器。

提示:如果您完全按原样使用此方法,并且您已经拥有包含资源等的App.xaml,那么您需要take a look at this too

答案 4 :(得分:22)

此代码应转到main方法。有关WPF中主要方法的更多信息,请查看here

[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(...);
    }
}

注意:以上方法假设您的流程/应用程序具有唯一名称。因为它使用进程名来查找是否存在任何现有处理器。因此,如果您的应用程序具有非常常见的名称(即:记事本),则上述方法将无效。

答案 5 :(得分:17)

好吧,我有一个一次性类,适用于大多数用例:

像这样使用:

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
}

答案 6 :(得分:14)

使用Mutex和IPC的新东西,并且还将任何命令行参数传递给正在运行的实例, WPF Single Instance Application

答案 7 :(得分:10)

作为标记答案参考的代码C# .NET Single Instance Application是一个很好的开始。

但是,我发现它不能很好地处理已经存在的实例打开模式对话框的情况,无论该对话框是托管对象(如另一个表格,如关于框),还是非托管对话框(就像使用标准.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)像这样修改App页面(并确保将其构建操作设置为page以便能够重新定义Main方法):

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);
        }
    }
}

答案 8 :(得分:9)

这是一个允许您拥有应用程序的单个实例的示例。当任何新实例加载时,它们将其参数传递给正在运行的主实例。

public partial class App : Application
{
    private static Mutex SingleMutex;
    public static uint MessageId;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        IntPtr Result;
        IntPtr SendOk;
        Win32.COPYDATASTRUCT CopyData;
        string[] Args;
        IntPtr CopyDataMem;
        bool AllowMultipleInstances = false;

        Args = Environment.GetCommandLineArgs();

        // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
        MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
        SingleMutex = new Mutex(false, "AppName");

        if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
        {
            new Main();
        }
        else if (Args.Length > 1)
        {
            foreach (Process Proc in Process.GetProcesses())
            {
                SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    2000, out Result);

                if (SendOk == IntPtr.Zero)
                    continue;
                if ((uint)Result != MessageId)
                    continue;

                CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));

                CopyData.dwData = IntPtr.Zero;
                CopyData.cbData = Args[1].Length*2;
                CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);

                Marshal.StructureToPtr(CopyData, CopyDataMem, false);

                Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    5000, out Result);

                Marshal.FreeHGlobal(CopyData.lpData);
                Marshal.FreeHGlobal(CopyDataMem);
            }

            Shutdown(0);
        }
    }
}

public partial class Main : Window
{
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        HwndSource Source;

        Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        Source.AddHook(new HwndSourceHook(Window_Proc));
    }

    private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
    {
        Win32.COPYDATASTRUCT CopyData;
        string Path;

        if (Msg == Win32.WM_COPYDATA)
        {
            CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
            Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);

            if (WindowState == WindowState.Minimized)
            {
                // Restore window from tray
            }

            // Do whatever we want with information

            Activate();
            Focus();
        }

        if (Msg == App.MessageId)
        {
            Handled = true;
            return new IntPtr(App.MessageId);
        }

        return IntPtr.Zero;
    }
}

public class Win32
{
    public const uint WM_COPYDATA = 0x004A;

    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int    cbData;
        public IntPtr lpData;
    }

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL             = 0x0000,
        SMTO_BLOCK              = 0x0001,
        SMTO_ABORTIFHUNG        = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
        SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}

答案 9 :(得分:8)

只是一些想法: 有些情况下,要求只有一个应用程序的实例不是“跛脚”,因为有些人会相信你。如果允许单个用户的多个应用实例访问数据库,那么数据库应用程序等就会变得更加困难(您知道,所有这些都会更新在用户的多个应用实例中打开的所有记录机器等)。 首先,对于“名称冲突的事情,不要使用人类可读的名称 - 使用GUID,或者更好的GUID +人类可读的名称。名称冲突的机会刚刚从雷达上掉下来,而互斥体并不关心正如有人指出的那样,DOS攻击会很糟糕,但是如果恶意的人已经遇到了获取互斥锁名称并将其合并到应用程序中的麻烦,那么无论如何你都是一个目标,并且还需要做更多的事情来保护你自己,而不仅仅是一个互斥名称。 此外,如果使用以下变体: 新的Mutex(true,“一些GUID加名称”,出AIsFirstInstance),你已经有了关于Mutex是否是第一个实例的指标。

答案 10 :(得分:6)

看起来有一种非常好的方法可以解决这个问题:

WPF Single Instance Application

这提供了一个可以添加的类,用于管理所有互斥和消息传递cruff,以简化您的实现,使其变得非常简单。

答案 11 :(得分:6)

以下代码是我的WCF命名管道解决方案,用于注册单实例应用程序。这很好,因为它还会在另一个实例尝试启动时引发一个事件,并接收另一个实例的命令行。

它面向WPF,因为它使用System.Windows.StartupEventHandler类,但这很容易修改。

此代码需要引用PresentationFrameworkSystem.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);
      }
    }
  }
}

答案 12 :(得分:6)

对这样一个看似简单的问题的答案很多。只是为了解决这个问题,我可以解决这个问题。

创建互斥锁可能很麻烦,因为JIT-er只会看到您将其用于代码的一小部分,并希望将其标记为垃圾收集准备就绪。它非常希望你认为你不会长时间使用那个Mutex。实际上,只要应用程序正在运行,您就想要挂在此Mutex上。告诉垃圾收集器单独留给Mutex的最好方法就是告诉它让它在不同代的车库收集中保持活力。例如:

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

我从这个页面提出了这个想法:http://www.ai.uga.edu/~mc/SingleInstance.html

答案 13 :(得分:5)

您永远不应该使用命名的互斥锁来实现单实例应用程序(或者至少不能用于生产代码)。恶意代码很容易DoS(Denial of Service)你的屁股......

答案 14 :(得分:4)

我找到了更简单的解决方案,类似于Dale Ragan,但略有修改。它几乎可以满足您的所有需求,并且基于标准的Microsoft WindowsFormsApplicationBase类。

首先,创建SingleInstanceController类,您可以在使用Windows窗体的所有其他单实例应用程序中使用它:

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中的第二个参数可以为null。在给定的示例中,窗口最大化。

答案 15 :(得分:4)

查看以下代码。这是一个非常简单的解决方案,可以防止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();
    }
}

答案 16 :(得分: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();
        }
    }

答案 17 :(得分:2)

更新2017-01-25。在尝试了几件事后,我决定使用VisualBasic.dll,它更容易,效果更好(至少对我而言)。我把我以前的答案作为参考...

就像参考一样,这是我没有传递参数的方式(我找不到任何理由这样做...我的意思是一个带有参数的应用程序,从一个实例传递到另一个实例) 。 如果需要文件关联,那么应该(每个用户的标准期望)为每个文档实例化应用程序。如果你必须将args传递给现有应用程序,我想我会使用vb dll。

没有传递args(只是单个实例应用程序),我更喜欢不注册新的Window消息而不是覆盖Matt Davis Solution中定义的消息循环。虽然添加VisualBasic dll并不是什么大不了的事,但我不想添加新的引用来做单实例应用。此外,我更喜欢使用Main实现一个新类,而不是从App.Startup覆盖调用Shutdown以确保尽快退出。

希望有人会喜欢它......或者会激发一点点:-)

项目启动类应设置为“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);
                        }
                    }
                }
            }
        }
    }
}

WindowHelper:

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);

答案 18 :(得分:2)

不使用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 - 语句中,然后将“#34;应用程序已运行”#34;。
这可能对某人有帮助。

答案 19 :(得分:1)

这是我使用的轻量级解决方案,它允许应用程序将现有窗口带到前台,而无需借助自定义窗口消息或盲目搜索进程名称。

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

static readonly string guid = "<Application Guid>";

static void Main()
{
    Mutex mutex = null;
    if (!CreateMutex(out mutex))
        return;

    // Application startup code.

    Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}

static bool CreateMutex(out Mutex mutex)
{
    bool createdNew = false;
    mutex = new Mutex(false, guid, out createdNew);

    if (createdNew)
    {
        Process process = Process.GetCurrentProcess();
        string value = process.Id.ToString();

        Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
    }
    else
    {
        string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
        Process process = null;
        int processId = -1;

        if (int.TryParse(value, out processId))
            process = Process.GetProcessById(processId);

        if (process == null || !SetForegroundWindow(process.MainWindowHandle))
            MessageBox.Show("Unable to start application. An instance of this application is already running.");
    }

    return createdNew;
}

编辑:你也可以静态存储和初始化互斥锁和createdNew,但是一旦你完成它就需要明确地处理/释放互斥锁。就个人而言,我更喜欢将互斥锁保持在本地,因为即使应用程序关闭而没有到达Main的末尾,它也会自动处理掉。

答案 20 :(得分:1)

请检查here中提出的解决方案,该解决方案使用信号灯确定现有实例是否已在运行,是否适用于WPF应用程序,并且可以使用TcpListener将参数从第二个实例传递到第一个已经运行的实例和一个TcpClient:

它也适用于.NET Core,不仅适用于.NET Framework。

答案 21 :(得分:1)

我无法在这里找到简短解决方案,所以我希望有人会喜欢这个:

更新2018-09-20

(顺便说一句。把代码放在你身上&#39;重新&#34; Program.cs&#34;

    using System.Diagnostics;

    static void Main()
    {
        Process ThisProcess = Process.GetCurrentProcess();
        Process[] AllProcesses = Process.GetProcessesByName(ThisProcess.ProcessName);
        if (AllProcesses.Length > 1)
        {
            //Don't put a MessageBox in here because the user could spam this MessageBox.
            return;
        }

//可选代码。如果你不想让某人经营你,那就是&#34; .exe&#34;用不同的名字:

        string exeName = AppDomain.CurrentDomain.FriendlyName;
        if (exeName != "the name of you're executable.exe") // If you try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe.
        {// You can add here a MessageBox if you want. To point users that the name got changed and maybe what the name should be or something like that^^ 
            MessageBox.Show("The executable name should be \"the name of you're executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        //Following Code is default code:
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

答案 22 :(得分:1)

基于命名互斥的方法不是跨平台的,因为命名互斥锁在Mono中不是全局的。基于进程枚举的方法没有任何同步并且可能导致不正确的行为(例如,同时启动的多个进程可能全部根据时序自终止)。基于窗口系统的方法在控制台应用程序中是不可取的。这个解决方案建立在Divin的答案之上,解决了所有这些问题:

using System;
using System.IO;

namespace TestCs
{
    public class Program
    {
        // The app id must be unique. Generate a new guid for your application. 
        public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";

        // The stream is stored globally to ensure that it won't be disposed before the application terminates.
        public static FileStream UniqueInstanceStream;

        public static int Main(string[] args)
        {
            EnsureUniqueInstance();

            // Your code here.

            return 0;
        }

        private static void EnsureUniqueInstance()
        {
            // Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
            string lockDir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                "UniqueInstanceApps");
            string lockPath = Path.Combine(lockDir, $"{AppId}.unique");

            Directory.CreateDirectory(lockDir);

            try
            {
                // Create the file with exclusive write access. If this fails, then another process is executing.
                UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);

                // Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
                // (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
                UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
                UniqueInstanceStream.Flush();
            }
            catch
            {
                throw new Exception("Another instance of the application is already running.");
            }
        }
    }
}

答案 23 :(得分: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();
    }
}

答案 24 :(得分:1)

您还可以使用免费工具集CodeFluent Runtime。它提供了一个SingleInstance类来实现单个实例应用程序。

答案 25 :(得分:1)

使用互斥解决方案:

using System;
using System.Windows.Forms;
using System.Threading;

namespace OneAndOnlyOne
{
static class Program
{
    static String _mutexID = " // generate guid"
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Boolean _isNotRunning;
        using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
        {
            if (_isNotRunning)
            {
                Application.Run(new Form1());
            }
            else
            {
                MessageBox.Show("An instance is already running.");
                return;
            }
        }
    }
}
}

答案 26 :(得分:0)

这是一个解决方案:

Protected Overrides Sub OnStartup(e As StartupEventArgs)
    Const appName As String = "TestApp"
    Dim createdNew As Boolean
    _mutex = New Mutex(True, appName, createdNew)
    If Not createdNew Then
        'app is already running! Exiting the application
        MessageBox.Show("Application is already running.")
        Application.Current.Shutdown()
    End If
    MyBase.OnStartup(e)
End Sub

答案 27 :(得分:0)

通常每当我们执行一个.exe时,每次创建一个单独的windows进程都有自己的地址空间,资源等等。但我们不希望这个标准,因为这会阻止我们创建单个流程。可以使用C#中的Mutex创建单实例应用程序is discussed in this article

此外,如果我们想把应用程序放在最前面,我们可以使用

来完成
 [DllImport("user32")]
 static extern IntPtr SetForegroundWindow(IntPtr hWnd);

答案 28 :(得分:0)

这就是我最终解决这个问题的方法。请注意,调试代码仍在那里进行测试。此代码位于App.xaml.cs文件中的OnStartup中。 (WPF)

text.setText(Html.fromHtml("your text"));

这可能有我尚未发现的问题。如果我碰到任何我会更新我的答案。

答案 29 :(得分: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;
                    }
                }
            }
        }
    }

答案 30 :(得分: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());
    }
}

答案 31 :(得分:0)

我在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);
}

答案 32 :(得分:0)

只需使用StreamWriter,这个怎么样?

System.IO.File.StreamWriter OpenFlag = null;   //globally

try
{
    OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
    Environment.Exit(0);
}

答案 33 :(得分:0)

[我在下面提供了控制台和wpf应用程序的示例代码。]

在创建指定的Mutex实例后,您只需要检查createdNew变量的值(例如下面的示例!)。

布尔值createdNew将返回 false:

  

如果名为“YourApplicationNameHere”的Mutex实例已经存在   在某个地方的系统上创建

布尔值createdNew将返回 true:

  

如果这是第一个名为“YourApplicationNameHere”的Mutex   系统

<小时/> 控制台应用程序 - 示例:

static Mutex m = null;

static void Main(string[] args)
{
    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        using (m = new Mutex(true, mutexName, out createdNew))
        {
            if (!createdNew)
            {
                Console.WriteLine("instance is alreday running... shutting down !!!");
                Console.Read();
                return; // Exit the application
            }

            // Run your windows forms app here
            Console.WriteLine("Single instance app is running!");
            Console.ReadLine();
        }


    }
    catch (Exception ex)
    {

        Console.WriteLine(ex.Message);
        Console.ReadLine();
    }
}

<强> WPF的示例:

public partial class App : Application
{
static Mutex m = null;

protected override void OnStartup(StartupEventArgs e)
{

    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        m = new Mutex(true, mutexName, out createdNew);

        if (!createdNew)
        {
            Current.Shutdown(); // Exit the application
        }

    }
    catch (Exception)
    {
        throw;
    }

    base.OnStartup(e);
}


protected override void OnExit(ExitEventArgs e)
{
    if (m != null)
    {
        m.Dispose();
    }
    base.OnExit(e);
}
}

答案 34 :(得分:0)

C#Winforms的节省时间的解决方案......

的Program.cs:

using System;
using System.Windows.Forms;
// needs reference to Microsoft.VisualBasic
using Microsoft.VisualBasic.ApplicationServices;  

namespace YourNamespace
{
    public class SingleInstanceController : WindowsFormsApplicationBase
    {
        public SingleInstanceController()
        {
            this.IsSingleInstance = true;
        }

        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e)
        {
            e.BringToForeground = true;
            base.OnStartupNextInstance(e);
        }

        protected override void OnCreateMainForm()
        {
            this.MainForm = new Form1();
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            string[] args = Environment.GetCommandLineArgs();
            SingleInstanceController controller = new SingleInstanceController();
            controller.Run(args);
        }
    }
}

答案 35 :(得分:0)

我在解决方案中使用Mutex来防止出现多个实例。

static Mutex mutex = null;
//A string that is the name of the mutex
string mutexName = @"Global\test";
//Prevent Multiple Instances of Application
bool onlyInstance = false;
mutex = new Mutex(true, mutexName, out onlyInstance);

if (!onlyInstance)
{
  MessageBox.Show("You are already running this application in your system.", "Already Running..", MessageBoxButton.OK);
  Application.Current.Shutdown();
}

答案 36 :(得分:0)

通常,这是我用于单实例Windows Forms应用程序的代码:

[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
}

答案 37 :(得分:0)

我最喜欢的解决方案来自MVP Daniel Vaughan: Enforcing Single Instance Wpf Applications

它使用MemoryMappedFile将命令行参数发送到第一个实例:

struct/classes

答案 38 :(得分:0)

基于 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.");
        Environment.Exit(1);
        return;
    }
    
    // Allow startup and continue with normal processing
    // ...
}