Click here to Skip to main content
15,897,181 members
Articles / Programming Languages / C# 3.5

Single Instance Application in C#

Rate me:
Please Sign up or sign in to vote.
4.87/5 (122 votes)
12 Dec 2004CPOL1 min read 586.3K   20K   131   131
An article demonstrating how to run a single instance of an application and activate previous one if already running.

Sample screenshot

Introduction

Single-instance an application means enabling the application to recognize, at startup, if another running instance of itself is already running, In this case, the new application stops its execution. Generally, for a form based application, the new instance activates (brings the application window to foreground) the previous instance, if its already running.

Using the code

To make a single instance application, add file SingleApplication.cs in your project. It adds a new class SingleApplication defined in namespace SingleInstance and adds the following code for a form based application to your startup code:

C#
static void Main() 
{
   SingleInstance.SingleApplication.Run(new FrmMain());
}

FrmMain is the main form class name. The Run method returns false if any other instance of the application is already running. For a console based application, call Run( ) without any parameter and check the return value, if it is true you can execute your application.

Using with console application:

C#
static void Main() 
{
   if(SingleInstance.SingleApplication.Run() == false)
   {
      return;
   }
   //Write your program logic here
}

History

  • June 29, 2003 - Initial release of article.
  • July 1, 2003 - Mutex support added.
  • December 11, 2004 - Improved performance on the basis of user feedback, special thanks to Mach005.
  • June 07. 2018 - Uploaded VS2015 project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Oracle
India India
Working with Oracle. Using C/C++, VC++, MFC, STL, C#, Java etc. on various platform like Windows, Unix, Macintosh etc. from last 13+ years to convert various type of requirements into running software components. My core expertise is multithreaded desktop product and large scale enterprises software development.

Comments and Discussions

 
GeneralRe: Room for improvement Pin
Manish K. Agarwal9-Jun-09 18:10
Manish K. Agarwal9-Jun-09 18:10 
GeneralRe: Room for improvement Pin
Anubhava Dimri9-Jun-09 18:30
Anubhava Dimri9-Jun-09 18:30 
GeneralSome comments Pin
Mach0052-Apr-04 1:33
Mach0052-Apr-04 1:33 
GeneralRe: Some comments Pin
Manish K. Agarwal2-Apr-04 3:24
Manish K. Agarwal2-Apr-04 3:24 
GeneralRe: Some comments Pin
Mach0054-Apr-04 23:12
Mach0054-Apr-04 23:12 
GeneralRe: Some comments Pin
szabelin10-May-04 7:45
szabelin10-May-04 7:45 
GeneralRe: Vast Improvement Pin
laduran16-Dec-05 6:56
laduran16-Dec-05 6:56 
GeneralRe: Vast Improvement Pin
Mach00517-Dec-05 20:55
Mach00517-Dec-05 20:55 
Here is a updated version.

Changed:

1. Scope is now an enum to support future scopes, not just local and global. Such as per assembly, per user like isolated storage works.

2. Options is now a flags enum, to support future extensibility.

3. I no longer pass the form object into the class. In many cases you dont even want to construct your main form if another instance is running. You may also wish to manipulate the Application object and choose to "run" a different way. Thus your Main should now look as follows:

static void Main()
{
  if (SingletonApplication.Run(SingletonApplicationScope.Local,
      SingletoneApplicationOptions.SwitchToCurrentInstance)
  {
    Form1 form1 = new Form1();
    Application.Run(form1);
  }
}


4. Added SingletonApplication.Exit. This allows you to release th mutex early. This is useful for restarting an application on an unhandled exception. You can call SingletonApplication.Exit before calling Application.Restart, thus allowing you to start another instance of yourself from yourself.

5. I don't use the .NET Framework Mutex object as .NET 1.1 does not support the required security to be able to use this in a Citrix or terminal services environment, including Windows XP multi-user mode. In .NET 2.0 they fixed this, by adding security to the Mutext object. Thus you can replace Win32Mutex by the .NET Mutex object on .NET 2.0, but not on .NET 1.1.

6. Switching to the current instance on .NET 1.1 results in all kinds of wierd exceptions and security issues under Citrix and terminal services. This has been completely redone to cater for the exceptions and avoid them where possible. For instance, iterating the processes, a process may have exited while you iterating over it and when you attempt to query info off it, an exception is thrown. Security exceptions are thrown for other user's processes. The Process object in .NET 1.1 used WMI, thus have to avoid using certain properties and methods to avoid exceptions in a locked down server.

#region SingletonApplicationScope

/// <summary>
/// Indicates the scope for a single instance application.
/// </summary>
public enum SingletonApplicationScope
{
    None,
    Local,
    Global
}

#endregion

#region SingletonApplicationOptions

/// <summary>
/// Specifies options that affect how a single instance application is run.
/// </summary>
[Flags]
public enum SingletonApplicationOptions
{
    None = 0x0,
    SwitchToCurrentInstance = 0x1
}

#endregion

#region SingletonApplication

/// <summary>
/// This class facilitates single instance applications running in either
/// the global scope (all users) or local scope (per user session).
/// </summary>
public sealed class SingletonApplication
{
    #region Public Members

    #region Run

    /// <summary>
    /// Run a singleton application.
    /// </summary>
    public static bool Run()
    {
        return InternalRun(SingletonApplicationScope.None, SingletonApplicationOptions.None, GetDefaultApplicationName());
    }

    public static bool Run(SingletonApplicationScope scope)
    {
        return InternalRun(scope, SingletonApplicationOptions.None, GetDefaultApplicationName());
    }

    public static bool Run(SingletonApplicationScope scope, SingletonApplicationOptions options)
    {
        return InternalRun(scope, options, GetDefaultApplicationName());
    }

    public static bool Run(SingletonApplicationScope scope, SingletonApplicationOptions options, string applicationName)
    {
        if (applicationName == null)
            throw new ArgumentNullException("applicationName");

        if (applicationName.IndexOf('\\') != -1)
        {
            throw new ArgumentException(
                Resources.ResourceManager[Resources.ArgumentExceptionSingletonApplicationName],
                "applicationName");
        }

        return InternalRun(scope, options, applicationName);
    }

    #endregion

    #region IsRunning

    /// <summary>
    /// Test if a single instance application is running.
    /// </summary>
    public static bool IsRunning()
    {
        return InternalIsRunning(SingletonApplicationScope.None, GetDefaultApplicationName());
    }

    public static bool IsRunning(SingletonApplicationScope scope)
    {
        return InternalIsRunning(scope, GetDefaultApplicationName());
    }

    public static bool IsRunning(SingletonApplicationScope scope, string applicationName)
    {
        if (applicationName == null)
            throw new ArgumentNullException("applicationName");

        if (applicationName.IndexOf('\\') != -1)
        {
            throw new ArgumentException(
                Resources.ResourceManager[Resources.ArgumentExceptionSingletonApplicationName],
                "applicationName");
        }

        return InternalIsRunning(scope, applicationName);
    }

    #endregion

    public static void Exit()
    {
        if (_mutex != null)
        {
            _mutex.Close();
            _mutex = null;
        }
    }

    /// <summary>
    /// Attempt to guarantee a unique/repeatable name, by using the full
    /// name of the entry assembly with version info, culture info and
    /// public key token. Using the entry assembly, rather than the
    /// executing assembly allows this class to be packaged in another
    /// assembly.
    /// </summary>
    public static string GetDefaultApplicationName()
    {
        return Assembly.GetEntryAssembly().GetName().FullName;
    }

    #endregion

    #region Private Members

    private static bool InternalRun(SingletonApplicationScope scope, SingletonApplicationOptions options, string applicationName)
    {
        //
        // Create mutex kernal object if it does not exist.
        //
        string mutexName = GetMutexName(scope, applicationName);
        bool createdNew;
        Win32Mutex mutex = new Win32Mutex(true, mutexName, out createdNew, true);
        if (!createdNew)
        {
            mutex.Close();
            if (IsOptionSet(options, SingletonApplicationOptions.SwitchToCurrentInstance))
                SwitchToCurrentInstance();
            return false;
        }
        // Static mutex reference, since it must be alive
        // as along as the application is alive.
        _mutex = mutex;
        return true;
    }

    private static bool InternalIsRunning(SingletonApplicationScope scope, string applicationName)
    {
        //
        // Create temporary mutex to test if application matching
        // specified name is currently running.
        //
        bool createdNew = false;
        string mutexName = GetMutexName(scope, applicationName);
        using (Win32Mutex mutex = new Win32Mutex(true, mutexName, out createdNew, true))
        {
            if (createdNew)
                mutex.ReleaseMutex();
        }
        return !createdNew;
    }

    private static string GetMutexName(SingletonApplicationScope scope, string applicationName)
    {
        // Create a valid mutex name.
        // Note: applicationName must not contain "\".
        // Add Global or Local scope prefix.
        return String.Concat(GetMutexScope(scope),
            typeof(SingletonApplication).GUID.ToString("D", CultureInfo.InvariantCulture),
            applicationName);
    }

    private static string GetMutexScope(SingletonApplicationScope scope)
    {
        switch (scope)
        {
            case SingletonApplicationScope.Local: return @"Local\";
            case SingletonApplicationScope.Global: return @"Global\";
        }
        return String.Empty;
    }

    private static bool IsOptionSet(SingletonApplicationOptions options, SingletonApplicationOptions option)
    {
        // Test if option flag is set.
        return ((options & option) == option);
    }

    private static void SwitchToCurrentInstance()
    {
        IntPtr currentInstanceWindowHandle = GetCurrentInstanceWindowHandle();
        if (currentInstanceWindowHandle != IntPtr.Zero)
            SetForegroundWindow(currentInstanceWindowHandle);
    }

    private static void SetForegroundWindow(IntPtr targetWindowHandle)
    {
        // Set the target window as the foreground window if it not the
        // current foreground window.
        IntPtr foregroundWindowHandle = User32.GetForegroundWindow();
        if (targetWindowHandle != foregroundWindowHandle)
        {
            // If the target window was created by a different thread to the
            // current thread, we need to attach the thread input of the
            // current thread to the target window thread before setting the
            // target window as the foreground window.
            uint targetWindowThreadId = User32.GetWindowThreadProcessId(targetWindowHandle, IntPtr.Zero);
            uint currentThreadId = Kernel32.GetCurrentThreadId();
            if (currentThreadId == targetWindowThreadId)
                User32.SetForegroundWindow(targetWindowHandle);
            else
            {
                User32.AttachThreadInput(currentThreadId, targetWindowThreadId, true);
                User32.SetForegroundWindow(targetWindowHandle);
                User32.AttachThreadInput(currentThreadId, targetWindowThreadId, false);
            }
        }
        // Restore the window if it is minimised.
        if (User32.IsIconic(targetWindowHandle))
            User32.ShowWindow(targetWindowHandle, User32.ShowWindowCommand.SW_RESTORE);
    }

    private static IntPtr GetCurrentInstanceWindowHandle()
    {
        Process currentProcess = Process.GetCurrentProcess();
        // Limit the candidate processes by process name.
        // Note: On earlier .NET Framework versions, Process.ProcessName was
        // limited to 15 characters, thus it is still neccessary to verify that
        // Process.MainModule.FileName exactly matches the current process'
        // full executable path.
        Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName);
        // Interate through the candidate processes looking for the first
        // valid candidate process.
        foreach (Process process in processes)
        {
            // Exclude the current process.
            if (process.Id != currentProcess.Id)
            {
                // Process.MainWindowHandle will return IntPtr.Zero if
                // the process has no main window in the current user
                // session. This elliminates all processes not for the
                // current user session, all processes that do not have a
                // valid window handle and any process that has exited.
                // This avoids a Win32Exception("Access Denied") if
                // Process.MainModule is called for a process in another
                // user session.
                //
                // Note: Process.WaitForInputIdle is not required since we
                // do not need the window handle of a process that is still
                // starting up. Process.WaitForInputIdle will throw a
                // Win32Exception("Access Denied") if the process exists in
                // another user session.
                IntPtr handle = process.MainWindowHandle;
                if (handle != IntPtr.Zero)
                {
                    try
                    {
                        // Verify that the candidate process was started from
                        // the exact same executable (full path).
                        if (process.MainModule.FileName == currentProcess.MainModule.FileName)
                            return handle;
                    }
                    catch(InvalidOperationException)
                    {
                        // Ignore the InvalidOperationException if the
                        // process was exited, otherwise rethrow the
                        // InvalidOperationException.
                        if (!process.HasExited)
                            throw;
                    }
                }
            }
        }
        return IntPtr.Zero;
    }

    private SingletonApplication()
    {
        // Prevent instances of the class from being constructed.
        throw new NotSupportedException();
    }

    private static Win32Mutex _mutex;

    #endregion
}

#endregion


Win32Mutex object:

/// <summary>
/// Microsoft Windows 32-bit native mutex kernel object.
///
/// This class is a replacement for the .NET Framework 1.1
/// System.Threading.Mutex class, providing and additional security
/// parameter to specific MUTEX_ALL_ACCESS to the 'Everyone' group.
///
/// The System.Threading.Mutex throws a security exception "Access Denied"
/// when attempting to create a named mutex from one user session,
/// to another user sessions that owns the already cerated named muetx,
/// where the user does not have rights on the machine for
/// MUTEX_ALL_ACCESS.
/// </summary>
public class Win32Mutex : WaitHandle
{
    #region Constructors

    public Win32Mutex()
    {
        bool createdNew;
        _handle = CreateMutex(false, null, false, out createdNew);
    }

    public Win32Mutex(bool initiallyOwned)
    {
        bool createdNew;
        _handle = CreateMutex(initiallyOwned, null, false, out createdNew);
    }

    public Win32Mutex(bool initiallyOwned, string name)
    {
        bool createdNew;
        _handle = CreateMutex(initiallyOwned, name, false, out createdNew);
    }

    public Win32Mutex(bool initiallyOwned, string name, out bool createdNew)
    {
        _handle = CreateMutex(initiallyOwned, name, false, out createdNew);
    }

    public Win32Mutex(bool initiallyOwned, string name, out bool createdNew, bool allAccess)
    {
        _handle = CreateMutex(initiallyOwned, name, allAccess, out createdNew);
    }

    #endregion

    #region Public Members

    public override IntPtr Handle
    {
        get { return _handle; }
        set {   _handle = value; }
    }

    public void ReleaseMutex()
    {
        if (_handle == WaitHandle.InvalidHandle)
            throw new ObjectDisposedException(GetType().FullName);
        ReleaseMutex(_handle);
    }

    #region WaitOne

    public override bool WaitOne()
    {
        if (_handle == WaitHandle.InvalidHandle)
            throw new ObjectDisposedException(GetType().FullName);
        return WaitForSingleObject(_handle, Kernel32.INFINITE);
    }

    public override bool WaitOne(int millisecondsTimeout, bool exitContext)
    {
        if (_handle == WaitHandle.InvalidHandle)
            throw new ObjectDisposedException(GetType().FullName);
        return WaitForSingleObject(_handle, millisecondsTimeout);
    }

    public override bool WaitOne(TimeSpan timeout, bool exitContext)
    {
        if (_handle == WaitHandle.InvalidHandle)
            throw new ObjectDisposedException(GetType().FullName);
        return WaitForSingleObject(_handle, (int)timeout.TotalMilliseconds);
    }

    #endregion

    #endregion

    #region Protected Members

    protected override void Dispose(bool explicitDisposing)
    {
        if (_handle != WaitHandle.InvalidHandle)
        {
            CloseHandle(_handle);
            _handle = WaitHandle.InvalidHandle;
        }
    }

    #endregion

    #region Private Members

    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
    private IntPtr CreateMutex(bool initiallyOwned, string name, bool allAccess, out bool createdNew)
    {
        Kernel32.SECURITY_ATTRIBUTES lpMutexAttributes = null;

        if (allAccess)
        {
            lpMutexAttributes = new Kernel32.SECURITY_ATTRIBUTES();
            lpMutexAttributes.nLength = Marshal.SizeOf(lpMutexAttributes);
            lpMutexAttributes.bInheritHandle = true;
            lpMutexAttributes.lpSecurityDescriptor = 0;

            int securityDescriptorSize = 0;
            if (!AdvApi32.ConvertStringSecurityDescriptorToSecurityDescriptor(
                SDDL_MUTEX_ALL_ACCESS_EVERYONE,
                AdvApi32.SDDL_REVISION_1,
                ref lpMutexAttributes.lpSecurityDescriptor,
                ref securityDescriptorSize))
            {
                throw new Win32Exception();
            }
        }

        try
        {
            IntPtr handle = Kernel32.CreateMutex(lpMutexAttributes, initiallyOwned, name);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (handle == IntPtr.Zero)
            {
                throw new Win32Exception(lastWin32Error);
            }
            createdNew = (lastWin32Error != Kernel32.ERROR_ALREADY_EXISTS);
            return handle;
        }
        finally
        {
            if (lpMutexAttributes != null)
            {
                if (Kernel32.LocalFree((IntPtr)lpMutexAttributes.lpSecurityDescriptor) != IntPtr.Zero)
                {
                    throw new Win32Exception();
                }
            }
        }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
    private bool WaitForSingleObject(IntPtr handle, int millisecondsTimeout)
    {
        int ret;
        if ((ret = Kernel32.WaitForSingleObject(handle, millisecondsTimeout)) == Kernel32.WAIT_FAILED)
        {
            throw new Win32Exception();
        }
        return (ret != Kernel32.WAIT_TIMEOUT);
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
    private void ReleaseMutex(IntPtr handle)
    {
        if (!Kernel32.ReleaseMutex(handle))
        {
            throw new Win32Exception();
        }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
    private void CloseHandle(IntPtr handle)
    {
        if (!Kernel32.CloseHandle(handle))
        {
            throw new Win32Exception();
        }
    }

    private const string SDDL_MUTEX_ALL_ACCESS_EVERYONE = "D:(A;NP;0x001f0001;;;WD)";
    private IntPtr _handle = WaitHandle.InvalidHandle;

    #endregion

-- modified at 2:59 Sunday 18th December, 2005
QuestionRe: Vast Improvement Pin
popoli1-Jan-06 21:51
popoli1-Jan-06 21:51 
QuestionRe: Vast Improvement Pin
JoeKau14-Mar-06 2:10
JoeKau14-Mar-06 2:10 
GeneralRe: Vast Improvement Pin
Dave Midgley1-Nov-06 22:01
Dave Midgley1-Nov-06 22:01 
QuestionRe: Some comments Pin
striker776-Jan-06 8:29
striker776-Jan-06 8:29 
GeneralAbsolutely Superb !!!!!! Pin
Maharishi Bhatia30-Jan-04 4:30
Maharishi Bhatia30-Jan-04 4:30 
Generalpb with notifyicon Pin
jcmag30-Jun-03 23:03
jcmag30-Jun-03 23:03 
GeneralRe: pb with notifyicon Pin
Manish K. Agarwal1-Jul-03 0:23
Manish K. Agarwal1-Jul-03 0:23 
GeneralRe: pb with notifyicon Pin
jcmag1-Jul-03 0:36
jcmag1-Jul-03 0:36 
GeneralRe: pb with notifyicon Pin
Manish K. Agarwal1-Jul-03 1:17
Manish K. Agarwal1-Jul-03 1:17 
GeneralRe: pb with notifyicon Pin
Frode N. Rosand12-Jul-03 16:39
Frode N. Rosand12-Jul-03 16:39 
GeneralRe: pb with notifyicon Pin
Manish K. Agarwal12-Jul-03 23:27
Manish K. Agarwal12-Jul-03 23:27 
GeneralRe: pb with notifyicon Pin
Mach0055-Apr-04 0:34
Mach0055-Apr-04 0:34 
GeneralRe: pb with notifyicon Pin
bschubarg30-Mar-06 9:08
bschubarg30-Mar-06 9:08 
GeneralI rated the article a 4, but... Pin
kbuchan30-Jun-03 4:38
kbuchan30-Jun-03 4:38 
GeneralRe: I rated the article a 4, but... Pin
Michael Potter30-Jun-03 6:08
Michael Potter30-Jun-03 6:08 
GeneralRe: I rated the article a 4, but... Pin
Manish K. Agarwal30-Jun-03 18:00
Manish K. Agarwal30-Jun-03 18:00 
GeneralProcess Name Pin
Michael Potter30-Jun-03 3:29
Michael Potter30-Jun-03 3:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.