What is the correct way to create a single-instance WPF application?

前端 未结 30 3183
耶瑟儿~
耶瑟儿~ 2020-11-21 05:14

Using C# and WPF under .NET (rather than Windows Forms or console), what is the correct way to create an application that can only be run as a single instance?

I kno

相关标签:
30条回答
  • 2020-11-21 05:35

    Update 2017-01-25. After trying few things, I decided to go with VisualBasic.dll it is easier and works better (at least for me). I let my previous answer just as reference...

    Just as reference, this is how I did without passing arguments (which I can't find any reason to do so... I mean a single app with arguments that as to be passed out from one instance to another one). If file association is required, then an app should (per users standard expectation) be instanciated for each doc. If you have to pass args to existing app, I think I would used vb dll.

    Not passing args (just single instance app), I prefer not registering a new Window message and not override the message loop as defined in Matt Davis Solution. Although it's not a big deal to add a VisualBasic dll, but I prefer not add a new reference just to do single instance app. Also, I do prefer instanciate a new class with Main instead of calling Shutdown from App.Startup override to ensure to exit as soon as possible.

    In hope that anybody will like it... or will inspire a little bit :-)

    Project startup class should be set as '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);
    
    0 讨论(0)
  • 2020-11-21 05:35

    Use mutex solution:

    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;
                }
            }
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-21 05:36

    You can also use the CodeFluent Runtime which is free set of tools. It provides a SingleInstance class to implement a single instance application.

    0 讨论(0)
  • 2020-11-21 05:38

    From here.

    A common use for a cross-process Mutex is to ensure that only instance of a program can run at a time. Here's how it's done:

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

    A good feature of Mutex is that if the application terminates without ReleaseMutex first being called, the CLR will release the Mutex automatically.

    0 讨论(0)
  • 2020-11-21 05:39

    I use Mutex in my solution for preventing multiple instances.

    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();
    }
    
    0 讨论(0)
  • 2020-11-21 05:40

    Named-mutex-based approaches are not cross-platform because named mutexes are not global in Mono. Process-enumeration-based approaches don't have any synchronization and may result in incorrect behavior (e.g. multiple processes started at the same time may all self-terminate depending on timing). Windowing-system-based approaches are not desirable in a console application. This solution, built on top of Divin's answer, addresses all these issues:

    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.");
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题