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

ID : 2584

viewed : 212

Tags : c#.netwpfmutexc#





Top 5 Answer for c# - What is the correct way to create a single-instance WPF application?

vote vote

98

Here is a very good article regarding the Mutex solution. The approach described by the article is advantageous for two reasons.

First, it does not require a dependency on the Microsoft.VisualBasic assembly. If my project already had a dependency on that assembly, I would probably advocate using the approach shown in another answer. But as it is, I do not use the Microsoft.VisualBasic assembly, and I'd rather not add an unnecessary dependency to my project.

Second, the article shows how to bring the existing instance of the application to the foreground when the user tries to start another instance. That's a very nice touch that the other Mutex solutions described here do not address.


UPDATE

As of 8/1/2014, the article I linked to above is still active, but the blog hasn't been updated in a while. That makes me worry that eventually it might disappear, and with it, the advocated solution. I'm reproducing the content of the article here for posterity. The words belong solely to the blog owner at Sanity Free Coding.

Today I wanted to refactor some code that prohibited my application from running multiple instances of itself.

Previously I had use System.Diagnostics.Process to search for an instance of my myapp.exe in the process list. While this works, it brings on a lot of overhead, and I wanted something cleaner.

Knowing that I could use a mutex for this (but never having done it before) I set out to cut down my code and simplify my life.

In the class of my application main I created a static named Mutex:

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

Having a named mutex allows us to stack synchronization across multiple threads and processes which is just the magic I'm looking for.

Mutex.WaitOne has an overload that specifies an amount of time for us to wait. Since we're not actually wanting to synchronizing our code (more just check if it is currently in use) we use the overload with two parameters: Mutex.WaitOne(Timespan timeout, bool exitContext). Wait one returns true if it is able to enter, and false if it wasn't. In this case, we don't want to wait at all; If our mutex is being used, skip it, and move on, so we pass in TimeSpan.Zero (wait 0 milliseconds), and set the exitContext to true so we can exit the synchronization context before we try to aquire a lock on it. Using this, we wrap our Application.Run code inside something like this:

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

So, if our app is running, WaitOne will return false, and we'll get a message box.

Instead of showing a message box, I opted to utilize a little Win32 to notify my running instance that someone forgot that it was already running (by bringing itself to the top of all the other windows). To achieve this I used PostMessage to broadcast a custom message to every window (the custom message was registered with RegisterWindowMessage by my running application, which means only my application knows what it is) then my second instance exits. The running application instance would receive that notification and process it. In order to do that, I overrode WndProc in my main form and listened for my custom notification. When I received that notification I set the form's TopMost property to true to bring it up on top.

Here is what I ended up with:

  • 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 (front side partial)
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;     } } 
vote vote

85

You could use the Mutex class, but you will soon find out that you will need to implement the code to pass the arguments and such yourself. Well, I learned a trick when programming in WinForms when I read Chris Sell's book. This trick uses logic that is already available to us in the framework. I don't know about you, but when I learn about stuff I can reuse in the framework, that is usually the route I take instead of reinventing the wheel. Unless of course it doesn't do everything I want.

When I got into WPF, I came up with a way to use that same code, but in a WPF application. This solution should meet your needs based off your question.

First, we need to create our application class. In this class we are going override the OnStartup event and create a method called Activate, which will be used later.

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

Second, we will need to create a class that can manage our instances. Before we go through that, we are actually going to reuse some code that is in the Microsoft.VisualBasic assembly. Since, I am using C# in this example, I had to make a reference to the assembly. If you are using VB.NET, you don't have to do anything. The class we are going to use is WindowsFormsApplicationBase and inherit our instance manager off of it and then leverage properties and events to handle the single instancing.

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

Basically, we are using the VB bits to detect single instance's and process accordingly. OnStartup will be fired when the first instance loads. OnStartupNextInstance is fired when the application is re-run again. As you can see, I can get to what was passed on the command line through the event arguments. I set the value to an instance field. You could parse the command line here, or you could pass it to your application through the constructor and the call to the Activate method.

Third, it's time to create our EntryPoint. Instead of newing up the application like you would normally do, we are going to take advantage of our SingleInstanceManager.

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

Well, I hope you are able to follow everything and be able use this implementation and make it your own.

vote vote

72

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.

vote vote

63

MSDN actually has a sample application for both C# and VB to do exactly this: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

The most common and reliable technique for developing single-instance detection is to use the Microsoft .NET Framework remoting infrastructure (System.Remoting). The Microsoft .NET Framework (version 2.0) includes a type, WindowsFormsApplicationBase, which encapsulates the required remoting functionality. To incorporate this type into a WPF application, a type needs to derive from it, and be used as a shim between the application static entry point method, Main, and the WPF application's Application type. The shim detects when an application is first launched, and when subsequent launches are attempted, and yields control the WPF Application type to determine how to process the launches.

  • For C# people just take a deep breath and forget about the whole 'I don't wanna include VisualBasic DLL'. Because of this and what Scott Hanselman says and the fact that this pretty much is the cleanest solution to the problem and is designed by people who know a lot more about the framework than you do.
  • From a usability standpoint the fact is if your user is loading an application and it is already open and you're giving them an error message like 'Another instance of the app is running. Bye' then they're not gonna be a very happy user. You simply MUST (in a GUI application) switch to that application and pass in the arguments provided - or if command line parameters have no meaning then you must pop up the application which may have been minimized.

The framework already has support for this - its just that some idiot named the DLL Microsoft.VisualBasic and it didn't get put into Microsoft.ApplicationUtils or something like that. Get over it - or open up Reflector.

Tip: If you use this approach exactly as is, and you already have an App.xaml with resources etc. you'll want to take a look at this too.

vote vote

58

This code should go to the main method. Look at here for more information about the main method in 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;      } } 

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

Note : Above methods assumes your process/application has a unique name. Because it uses process name to find if any existing processors. So, if your application has a very common name (ie: Notepad), above approach won't work.

Top 3 video Explaining c# - What is the correct way to create a single-instance WPF application?







Related QUESTION?