代码之家  ›  专栏  ›  技术社区  ›  VolkerK

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

  •  584
  • VolkerK  · 技术社区  · 17 年前

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

    我知道这和一个叫做互斥的神秘事物有关,我很少能找到一个愿意停下来解释其中一个的人。

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

    35 回复  |  直到 7 年前
        1
  •  467
  •   Matt Davis    12 年前

    这是一个非常好的 article 关于互斥解决方案。本文所描述的方法有两个优点。

    首先,它不需要依赖Microsoft.VisualBasic程序集。如果我的项目已经依赖于这个组件,我可能会提倡使用公认的答案中所示的方法。但事实上,我不使用microsoft.visualBasic程序集,我也不想在我的项目中添加不必要的依赖项。

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


    更新

    截至2014年8月1日,我链接到上面的文章仍然是活跃的,但是博客已经有一段时间没有更新了。这让我担心,最终它可能会消失,随之而来的是所提倡的解决方案。我在为子孙后代复制这篇文章的内容。这些词只属于博客所有者 Sanity Free Coding .

    今天我想重构一些禁止我的应用程序的代码 运行自身的多个实例。

    以前我用过 System.Diagnostics.Process 搜索 进程列表中myapp.exe的实例。在这个过程中, 带来了很多开销,我想要更干净的东西。

    知道我可以使用互斥(但从来没有这样做过) 之前)我开始减少我的代码并简化我的生活。

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

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

    拥有一个命名的互斥体允许我们在 多线程和多进程,这正是我所寻找的魔力 为了。

    Mutex.WaitOne 有一个重载,它为我们指定了一段时间 等待。因为我们实际上不想同步我们的代码 (更多的只是检查它是否在使用中)我们使用重载 两个参数: Mutex.WaitOne(Timespan timeout, bool exitContext) . 如果可以输入则返回“真”,否则返回“假”。 在这种情况下,我们根本不想等待;如果我们的互斥正在 使用,跳过它,继续,所以我们传入时间跨度。0(等待0 毫秒),并将exitContext设置为true,以便退出 同步上下文,然后我们尝试获取它的锁。使用 这样,我们就可以包装我们的应用程序。在这样的代码中运行:

    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 以我的主要形式聆听我的习俗 通知。当我收到通知时,我设置了 最重要的属性为“真”以使其在顶部显示。

    以下是我的结论:

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

    您可以使用mutex类,但很快就会发现您需要实现代码来传递参数,这样您自己就可以了。嗯,当我阅读时,我在WinForms中编程时学会了一个技巧 Chris Sell's book . 这个技巧使用了框架中已经存在的逻辑。我不了解你,但是当我了解到我可以在框架中重用的东西时,这通常是我走的路线,而不是重新设计轮子。当然,除非它不能满足我的需要。

    当我进入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方法的调用将它传递给应用程序。

    第三,是时候创建入口点了。我们将利用我们的单实例管理器,而不是像通常那样更新应用程序。

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

    好吧,我希望您能够跟踪所有的事情,并且能够使用这个实现并使之成为您自己的实现。

        3
  •  77
  •   abatishchev Karl Johan    15 年前

    here .

    跨进程互斥体的一个常见用途是确保一次只能运行程序的实例。完成方法如下:

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

    互斥体的一个好特性是,如果应用程序在没有首先调用release mutex的情况下终止,CLR将自动释放互斥体。

        4
  •  52
  •   Community Mohan Dere    9 年前

    实际上,msdn有一个用于C和VB的示例应用程序来执行以下操作: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

    最常见、最可靠的技术 用于开发单个实例 检测是使用Microsoft.NET 框架远程处理基础设施 (系统远程处理)。微软.NET 框架(2.0版)包括 类型,WindowsFormsApplicationBase, 它封装了所需的 远程处理功能。并入 将此类型输入到WPF应用程序中, 类型需要从中派生,并且 用作应用程序之间的垫片 静态入口点方法、主方法和 WPF应用程序的应用程序 类型。垫片检测到 首先启动应用程序,并且 当后续发射 尝试,并生成控制WPF的 应用程序类型以确定如何 处理发布。

    • 对于C人们只需深呼吸,然后忘记整个“我不想包括VisualBasic DLL”。因为 this 什么 Scott Hanselman says 事实上,这几乎是解决问题的最干净的方法,并且是由比您更了解框架的人设计的。
    • 从可用性的角度来看,事实是,如果您的用户正在加载一个应用程序,并且它已经打开,并且您给了他们一条错误消息,比如 'Another instance of the app is running. Bye' 那他们就不会是一个非常快乐的用户了。您只需(在GUI应用程序中)切换到该应用程序并传递所提供的参数—或者如果命令行参数没有意义,则必须弹出可能最小化的应用程序。

    框架已经对此提供了支持——只是某个白痴将DLL命名为 Microsoft.VisualBasic 它没有被放进去 Microsoft.ApplicationUtils 或者类似的。越过它-或者打开反射镜。

    提示:如果您按原样使用这种方法,并且已经有了一个包含资源等的app.xaml,那么您将希望 take a look at this too .

        5
  •  21
  •   CharithJ    11 年前

    此代码应转到主方法。看 here 有关WPF中主要方法的详细信息。

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

    注: 以上方法假定您的进程/应用程序具有唯一的名称。因为它使用进程名来查找是否有任何现有的处理器。因此,如果您的应用程序有一个非常常见的名称(即:记事本),那么上述方法将不起作用。

        6
  •  16
  •   natenho    9 年前

    好吧,我有一个一次性类,可以很容易地用于大多数用例:

    这样使用:

    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
    }
    
        7
  •  12
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    一个新的方法是使用mutex和ipc,并将任何命令行参数传递给正在运行的实例。 WPF Single Instance Application .

        8
  •  9
  •   Simon Mourier    11 年前

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

    但是,我发现它不能很好地处理已经存在的实例打开模式对话框时的情况,无论该对话框是托管对话框(像其他窗体,如about框)还是非托管对话框(像OpenFileDialog,即使使用标准.NET类)。使用原始代码时,主窗体被激活,但模式窗体保持不活动状态,这看起来很奇怪,而且用户必须单击它才能继续使用该应用程序。

    所以,我已经创建了一个单实例实用程序类来自动处理WinForms和WPF应用程序的所有这些问题。

    WiFrices :

    1)如下修改程序类:

    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)像这样修改应用程序页面(并确保将其生成操作设置为页面,以便能够重新定义主方法):

    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);
            }
        }
    }
    
        9
  •  9
  •   Nathan Moinvaziri Cloud    10 年前

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

    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);
    }
    
        10
  •  8
  •   Bruce    16 年前

    只是一些想法: 在某些情况下,要求只有一个应用程序实例不是像您认为的那样“跛脚”。如果一个应用允许一个用户访问一个数据库的多个应用实例(你知道,所有更新用户机器上应用程序多个实例中打开的所有记录的操作,等等),那么数据库应用程序等的难度就大得多。 首先,对于“名称冲突”的事情,不要使用人可读的名称-使用一个guid,或者更好的是guid+人可读的名称。名称冲突的可能性刚刚从雷达上消失,而互斥体则不在乎。正如有人指出的那样,DoS攻击会很糟糕,但是如果恶意的人遇到了获取互斥体名称并将其合并到他们的应用程序中的麻烦,那么不管怎样,你基本上是一个目标,你必须做更多的工作来保护自己,而不仅仅是篡改互斥体名称。 此外,如果使用以下变量: 新的互斥体(true,“some guid plus name”,out aifirstinstance),您已经有了互斥体是否是第一个实例的指示器。

        11
  •  6
  •   Peter    16 年前

    对于这样一个看似简单的问题,有这么多答案。只是稍微改变一下,这是我解决这个问题的方法。

    创建互斥体可能很麻烦,因为jit-er只看到您在代码的一小部分中使用它,并希望将其标记为准备好进行垃圾收集。它非常想聪明,你认为你不会在这么长时间内使用这个互斥。实际上,只要您的应用程序正在运行,您就希望保持这个互斥体。告诉垃圾收集器不要使用mutex的最好方法是告诉它在不同代的车库收集之外保持它的活动状态。例子:

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

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

        12
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    看起来有一个很好的方法来处理这个问题:

    WPF Single Instance Application

    这提供了一个类,您可以添加该类来管理所有互斥和消息传递错误,以简化实现,使其变得简单。

        13
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    下面的代码是我注册单个实例应用程序的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);
          }
        }
      }
    }
    
        14
  •  5
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    您不应该使用命名的互斥体来实现单个实例应用程序(或者至少不用于生产代码)。恶意代码很容易做到( Denial of Service 你的屁股…

        15
  •  4
  •   Sergey Aldoukhov    16 年前

    这是我用的。它结合了进程枚举来执行切换和互斥,以防止“活动的点击器”:

    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();
            }
        }
    
        16
  •  4
  •   Mikhail Semenov    15 年前

    我发现了更简单的解决方案,类似于DaleRagan的,但有点修改。它几乎可以满足您的所有需要,并且基于标准的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();         
            }
        }
    }
    

    程序和单实例控制器的网络解决方案都应参考Microsoft.VisualBasic。如果您只想在用户尝试重新启动正在运行的程序时将正在运行的应用程序作为普通窗口重新激活,则singleInstanceController中的第二个参数可以为空。在给定的示例中,窗口最大化。

        17
  •  4
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    看看下面的代码。这是一个伟大而简单的解决方案,可以防止一个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();
        }
    }
    
        18
  •  2
  •   newbieguy    9 年前

    但不使用互斥,简单回答:

    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 -语句并将“应用程序已在运行”。
    这可能对某人有帮助。

        19
  •  2
  •   Eric Ouellet    9 年前

    更新2017-01-25。 在尝试了几件事情之后,我决定使用visualbasic.dll,它更简单,工作更好(至少对我来说)。我让我以前的答案作为参考…

    就像参考文献一样,这是我在不传递参数的情况下所做的(我找不到任何理由这样做…我指的是一个单独的应用程序,其参数可以从一个实例传递到另一个实例)。 如果需要文件关联,则应为每个文档安装一个应用程序(根据用户的标准期望)。如果必须将args传递给现有的应用程序,我想我将使用vb dll。

    不传递args(只是单实例应用程序),我更喜欢不注册新的窗口消息,也不重写MattDavis解决方案中定义的消息循环。虽然添加一个VisualBasic DLL并不是什么大问题,但是我不希望只添加一个新的引用来执行单实例应用程序。此外,我更喜欢用main来声明一个新类,而不是从app.startup override调用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);
    
        20
  •  1
  •   Cornel Marian    12 年前

    使用互斥解决方案:

    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;
                }
            }
        }
    }
    }
    
        21
  •  1
  •   Jason Lim    12 年前

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

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

    编辑:您还可以静态地存储和初始化互斥体并创建新的互斥体,但完成后,您需要显式地释放/释放互斥体。就我个人而言,我更喜欢将互斥体保持在本地,因为即使应用程序关闭而没有到达主系统的末尾,互斥体也会自动被处理掉。

        22
  •  1
  •   Antoine Diekmann    11 年前

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

        23
  •  1
  •   Siarhei Kuchuk    10 年前

    这是通过事件实现的相同的事情。

    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
  •   A.T.    8 年前

    基于命名互斥体的方法不是跨平台的,因为命名互斥体在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.");
                }
            }
        }
    }
    
        25
  •  1
  •   Deniz    7 年前

    我找不到 短解 我希望有人会喜欢这个:

    更新日期:2018-09-20

    (顺便说一下,把代码放在 “程序CS” )

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

    //可选代码。如果不希望有人运行,请使用不同名称的“.exe”:

            string exeName = AppDomain.CurrentDomain.FriendlyName;
            if (exeName != "the name of you're executable.exe") // If u 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 MassageBox 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());
        }
    
        26
  •  0
  •   Martin Bech    11 年前

    我向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);
    }
    
        27
  •  0
  •   Vikram    11 年前

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

    此外,如果我们想让应用程序处于顶层,我们可以使用

     [DllImport("user32")]
     static extern IntPtr SetForegroundWindow(IntPtr hWnd);
    
        28
  •  0
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    通常,这是我用于单个实例的代码 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
    }
    
        29
  •  0
  •   elixenide Ren    10 年前

    以下是一个解决方案:

    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
    
        30
  •  0
  •   pStan    10 年前

    我就是这样处理这个问题的。请注意,调试代码仍在测试中。此代码在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();
    
            }
    

    这可能有一些我还没有发现的问题。如果我遇到任何问题,我会更新我的答案。