Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / C#
Article

Single Process Instance Object

Rate me:
Please Sign up or sign in to vote.
4.85/5 (43 votes)
9 Oct 2002Public Domain4 min read 269K   2K   84   63
An object that enforces a rule that only one instance of a program can be running at a time.

Introduction

Enforcing a rule that only one instance of process is running is an interesting task. There are many ways to code the algorithm on Win32. I solved the problem by creating a reusable C# class that encapsulates one of the quirks of the Win32 API and makes the identification of another "same" process more certain.

This article was triggered (and borrows heavily from) another article written by Marc Clifton, "Detect if another process is running and bring it to the foreground" which is also available on CodeProject. When implementing Marc's code I ran into a few issues:

  • The code was not in an easily reusable format.
  • Identifying processes by name alone can be error prone when the name is common.
  • The multi-threaded nature of Win32 could conceivably have an undesirable effect on the reporting of an initial "same" process instance.
  • Long assembly names trigger partially hidden process names in Win32.

Solution

In order to enhance the detection of other instances of a process, I decided to use a named mutex synchronization object. By definition, Win32 dictates that only one instance of a named mutex will exist on a system and any given time. Win32 further guarantees that only one thread will own the mutex at any given time. Since a mutex can cross the process boundary, it became a good choice for identifying existing processes.

I encapsulated the mutex in a class (SingleProgramInstance) and had the constructors attempt to create and gain ownership of the mutex immediately. Failure to gain ownership indicates the existence of another "same" process currently running. If the object successfully acquires ownership the program will hold onto it during its operation and therefore, announce to the system that it is the initial one and only process. A property (IsSingleInstance) is used to indicate this state.

There are two constructors for the object. One of the constructors accepts no parameters and uses the current assembly name to name the mutex. Because assembly names can be common between different applications, I created a second constructor that accepts a string parameter. The object appends this string to the assembly name to help differentiate it from other applications. The programmer can pass in a few meaningless characters to greatly reduce the likelihood of name duplication.

UI Concerns

When another "same" process is identified, it is usually nice to present the previous instance to the user before terminating the redundant process. I separated this code from the detection code because it may not be desirable in all circumstances (i.e. tray icon applications). Essentially, I ask Windows for the all of the processes that match the current process name and use some Win32 Interop calls to restore and bring the process to the foreground. Herein lies a small quirk with Win32.

Only the first 15 characters of a process name are made available when you ask for a process name. Unfortunately, when you ask for all of the processes of a certain name (via Process.GetProcessesByName), Windows uses the whole name (which may exceed 15 characters) when performing the test. That leaves us the choice of looping through all the processes ourselves and testing only the first 15 characters or find out our full process name and ask windows to return only those that match. Since the first 15 characters may not be unique enough for a good test, I choose the second approach. Luckily, it seems that the assembly name and the full (hidden) process name are one and the same. Using the assembly name, Win32 returns only the processes that match the full process name and I can easily ignore my own process by testing the process id.

Cleanup

Since this object is reliant on a system resource (mutex) a good method for clean up should be employed. I chose to use the IDisposable interface to guarantee a deterministic release of the mutex object. When used properly, the mutex will be released immediately upon program termination so that an additional process can again be started. This should happen anyway due to the nature of a mutex but that is no excuse for sloppy code. Below is a example of the proper use of this object. Notice that the using statement neatly wraps the Application.Run().

C#
using SpecialServices;

....

[STAThread]
static void Main() 
{
    using(SingleProgramInstance spi = new SingleProgramInstance("x5k6yz"))
    {
        if (spi.IsSingleInstance)
        {
            Application.Run(new Form1());
        }
        else
        {
            spi.RaiseOtherProcess();
        }
    }
}

....

Object Source

C#
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;

namespace SpecialServices
{
    //SingleProgamInstance uses a mutex synchronization 
    //object to ensure that only one copy of process is running 
    //at a particular time.  It also allows for UI identification
    // of the intial process by bringing that window to the foreground.
public class SingleProgramInstance : IDisposable
{

    //Win32 API calls necesary to raise an unowned processs main window
    [DllImport("user32.dll")] 
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] 
    private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
    [DllImport("user32.dll")] 
    private static extern bool IsIconic(IntPtr hWnd);

    private const int SW_RESTORE = 9;

    //private members 
    private Mutex _processSync;
    private bool _owned = false;


    public SingleProgramInstance()
    {   
        //Initialize a named mutex and attempt to
        // get ownership immediately 
        _processSync = new Mutex(
            true, // desire intial ownership
            Assembly.GetExecutingAssembly().GetName().Name,
            out _owned);
    }

    public SingleProgramInstance(string identifier)
    {   
        //Initialize a named mutex and attempt to
        // get ownership immediately.
        //Use an addtional identifier to lower
        // our chances of another process creating
        // a mutex with the same name.
        _processSync = new Mutex(
            true, // desire intial ownership
            Assembly.GetExecutingAssembly().GetName().Name + identifier,
            out _owned);
    }

    ~SingleProgramInstance()
    {
        //Release mutex (if necessary) 
        //This should have been accomplished using Dispose() 
        Release();
    }

    public bool IsSingleInstance
    {
        //If we don't own the mutex than
        // we are not the first instance.
        get {return _owned;}
    }

    public void RaiseOtherProcess()
    {
        Process proc = Process.GetCurrentProcess();
        // Using Process.ProcessName does not function properly when
        // the actual name exceeds 15 characters. Using the assembly 
        // name takes care of this quirk and is more accruate than 
        // other work arounds.
        string assemblyName = 
            Assembly.GetExecutingAssembly().GetName().Name;
        foreach (Process otherProc in 
            Process.GetProcessesByName(assemblyName))
        {
            //ignore "this" process
            if (proc.Id != otherProc.Id)
            {
                // Found a "same named process".
                // Assume it is the one we want brought to the foreground.
                // Use the Win32 API to bring it to the foreground.
                IntPtr hWnd = otherProc.MainWindowHandle;
                if (IsIconic(hWnd))
                {
                    ShowWindowAsync(hWnd,SW_RESTORE);
                }
                SetForegroundWindow(hWnd);
                break;
            }
        }
    }

    private void Release()
    {
        if (_owned)
        {
            //If we own the mutex than release it so that
            // other "same" processes can now start.
            _processSync.ReleaseMutex();
            _owned = false;
        }
    }

#region Implementation of IDisposable
    public void Dispose()
    {
        //release mutex (if necessary) and notify 
        // the garbage collector to ignore the destructor
        Release();
        GC.SuppressFinalize(this);
    }
#endregion
}
}

Summary

I would like to thank Marc Clifton for his insightful article that drove me to create the above solution. Hopefully, others will expand upon what we have done to produce an even more solid solution.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Chief Technology Officer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionmutex is not working Pin
kornisch10-Oct-22 0:11
kornisch10-Oct-22 0:11 
QuestionHow To Make It Restart? Pin
Andy Cartwright3-Jan-16 0:55
Andy Cartwright3-Jan-16 0:55 
AnswerWorking classes for tray / hidden programs Pin
Vlad Schnakovszki21-Jan-13 13:06
Vlad Schnakovszki21-Jan-13 13:06 
GeneralGood process but I 've bug Pin
theanhsts24-Jan-08 16:04
theanhsts24-Jan-08 16:04 
GeneralNice article, but one bug [modified] Pin
jeffb428-Nov-06 8:43
jeffb428-Nov-06 8:43 
GeneralRe: Nice article, but one bug Pin
jeffb428-Nov-06 8:46
jeffb428-Nov-06 8:46 
GeneralRe: Nice article, but one bug Pin
Zoltan Balazs9-Jan-07 2:56
Zoltan Balazs9-Jan-07 2:56 
GeneralRe: Nice article, but one bug Pin
jeffb4215-Mar-07 11:05
jeffb4215-Mar-07 11:05 
GeneralRe: Nice article, but one bug Pin
melchizedek1-Jun-07 8:23
melchizedek1-Jun-07 8:23 
GeneralRe: Nice article, but one bug Pin
The Last Gunslinger18-Jul-07 15:05
The Last Gunslinger18-Jul-07 15:05 
GeneralRe: Nice article, but one bug [modified] Pin
Daniele Rota Nodari14-Dec-11 23:06
Daniele Rota Nodari14-Dec-11 23:06 
Hi.

If you want a reference to the assembly that originated the running process, you should use Assembly.GetEntryAssembly().
This approach should work from any method within any assembly loaded by that process (actually it does inside my Windows applications Wink | ;) ).

Daniele.
GeneralVery good project Pin
Flow8413-Jul-06 2:20
Flow8413-Jul-06 2:20 
QuestionHow to implement... Pin
h4rdw4re119-Jun-06 0:29
h4rdw4re119-Jun-06 0:29 
Generalfinalizer Pin
swidenerccf11-May-06 10:31
swidenerccf11-May-06 10:31 
GeneralRe: finalizer [modified] Pin
CKret4-Dec-07 2:53
CKret4-Dec-07 2:53 
GeneralRe: finalizer Pin
Matthew Wilcoxson29-Oct-08 7:17
Matthew Wilcoxson29-Oct-08 7:17 
GeneralShowWindowAsync doesn't work in 98 Pin
Dave Midgley3-Jan-06 6:14
Dave Midgley3-Jan-06 6:14 
GeneralThanks Pin
Joe Pardue17-Dec-05 4:19
Joe Pardue17-Dec-05 4:19 
GeneralSuggested improvement Pin
Phil J Pearson12-Dec-05 8:26
Phil J Pearson12-Dec-05 8:26 
GeneralRe: Suggested improvement Pin
Stan Shillis27-Dec-05 8:08
Stan Shillis27-Dec-05 8:08 
GeneralRe: Suggested improvement Pin
Phil J Pearson27-Dec-05 8:27
Phil J Pearson27-Dec-05 8:27 
GeneralRe: Suggested improvement Pin
saud_ad15-Feb-08 0:01
saud_ad15-Feb-08 0:01 
GeneralGood Work Pin
junkmegently6-Nov-05 14:47
junkmegently6-Nov-05 14:47 
GeneralNice Class Pin
David Roh20-Oct-05 5:27
David Roh20-Oct-05 5:27 
GeneralVB Pin
laxwy9-Sep-05 7:43
laxwy9-Sep-05 7:43 

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.