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

Running a Single Instance per User

Rate me:
Please Sign up or sign in to vote.
2.69/5 (11 votes)
14 Mar 2006CPOL2 min read 50.5K   5   13   12
How to run one instance per user of an application on a machine with multiple users logged in.

Introduction

A quick Google search will list a variety of ways to limit an application to running only one instance per computer, but what about the case where there are multiple users who are logged in simultaneously? How do you allow each user to only run one instance?

Note: The condition described by this article is when you have multiple users on the same computer, and not the case where you have a single user logged into multiple computers that are networked together.

Limiting the Application to a Single Instance

Limiting the application to only running a single instance can be done simply by getting the current process and comparing the process Id for each process with the same name to that of the current process. If another process is found, show it instead. This is a fairly well documented method of limiting the application to only one instance.

Example code is shown below:

C#
// The main entry point to the application
[STAThread]     
static void Main() 
{
    // Look for a running instance
    Process runningInstance = GetRunningInstance();
    if (runningInstance == null)
    {
        // Create the user instance property and 
        // set it on the main form
        Form mainForm = new Form1();
        UserInstance user = new UserInstance(mainForm.Handle);
            
        // Run the application
        Application.Run(mainForm);
            
        // dispose of the user instance 
        user.Dispose();
    }
    else
    {
        // There is another instance of this process; 
        // show it instead
        ShowWindow (runningInstance.MainWindowHandle, 1);
    }
}
    
public static Process GetRunningInstance()
{
    // Get the current process and all processes 
    // with the same name
    Process current = Process.GetCurrentProcess();
    Process[] processes = 
        Process.GetProcessesByName(current.ProcessName);
            
    //Loop through the running processes with the same name
    foreach (Process process in processes)
    {
        // Looking for a process with a different ID and
        // the same username
        if ((process.Id != current.Id) &&
            UserInstance.IsSameUser(process.MainWindowHandle))
        {
            //Return the other process instance.
            return process;
        }
    }
    return null;
}
    
// Import DLL methods from user32
[DllImport ("user32.dll", CharSet = CharSet.Auto)]
public static extern bool ShowWindow (IntPtr hWnd, int cmdShow);   

Starting an Instance for a Different User

Okay, simple enough, but what if user "Mom" is running the application and then user "Dad" switches to his desktop and wants to start the same application? The application will run briefly, detect the already existing instance and exit.

My first attempt was to try to handle this by comparing the value of System.Environment.Username to process.StartInfo.EnvironmentVariables["username"]. If they are equal, then the instance is running for the same user. This did not work because Windows XP resets the process to belong to the active user.

My work-around was to set a window property on the main window that includes the username. This requires importing a couple more methods from the User32 DLL and disposing of the object so you can release the allocated memory. To handle this, I created a simple class that sets the property upon construct and has a static method to determine if this is the same user who opened the original instance.

C#
public class UserInstance: IDisposable
{
    private static string myPropertyName = 
            "USERNAME." + System.Environment.UserName;

    private IntPtr handle = IntPtr.Zero; 
    private bool disposed = false;

    public UserInstance(IntPtr hWin)
    {
        this.handle = hWin;
        SetProp(handle, myPropertyName, (int)hWin);
    }
      
    ~UserInstance()
    {
        Dispose();
    }

    public void Dispose()
    {
        if (!disposed)
        {
            // remove the property
            RemoveProp(handle, myPropertyName);
            disposed = true;
        }        
    }

    static public bool IsSameUser(IntPtr hWin)
    {
        if ((int)hWin == 0) 
        {
            // If the main window has not been started yet
            // then the user clicked too many times.
            return true;
        }
        else
        {
            // If the property does not exist then this 
            // is a different user
            int ptr = GetProp(hWin, myPropertyName);
            return (ptr != 0);
        }
    }

// Import DLL methods from user32
[DllImport("user32", CharSet=CharSet.Auto)]
public extern static int GetProp(IntPtr hwnd, string lpString);
[DllImport("user32", CharSet=CharSet.Auto)]
public extern static bool SetProp(IntPtr hwnd, string lpString, int hData);
[DllImport("user32", CharSet=CharSet.Auto)]
public extern static int RemoveProp(IntPtr hwnd, string lpString);

}//end class  

Notes

For the sake of simplicity, the code presented here looks a little different from the sample code. This was done to keep the article brief. Conceptually, they are the same.

References

License

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


Written By
Web Developer
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

 
GeneralCode Pin
ankur14013-Jun-16 21:14
ankur14013-Jun-16 21:14 
GeneralNo source code Pin
s.kammonah27-Apr-10 3:57
s.kammonah27-Apr-10 3:57 
Generalor you could use a mutex! Pin
jmw2trinculo17-Jun-08 2:23
jmw2trinculo17-Jun-08 2:23 
GeneralGood Job! Pin
Oleksiy Sokolovskiy28-Sep-07 4:36
Oleksiy Sokolovskiy28-Sep-07 4:36 
QuestionOne hole Pin
KenNashua13-Jul-07 3:28
KenNashua13-Jul-07 3:28 
AnswerRe: One hole Pin
smallwisdom13-Jul-07 8:25
smallwisdom13-Jul-07 8:25 
Generalhihi...How to capture exit event of multiple ID with the same process Pin
roychoo26-Jan-07 0:37
roychoo26-Jan-07 0:37 
GeneralAnother Method Pin
DStiny19-Dec-06 9:56
DStiny19-Dec-06 9:56 
GeneralRe: Another Method Pin
smallwisdom13-Jul-07 8:31
smallwisdom13-Jul-07 8:31 
Nice! Thanks for posting.

The method I used was a work-around for .Net 1.1 which does not have the Semaphore class.

Shannon Young

NewsWrong category Pin
Johann Gerell15-Mar-06 19:59
Johann Gerell15-Mar-06 19:59 
GeneralThis is not a single instance per user... Pin
Thomas Weidenmueller14-Mar-06 22:53
Thomas Weidenmueller14-Mar-06 22:53 
GeneralRe: This is not a single instance per user... Pin
smallwisdom15-Mar-06 5:33
smallwisdom15-Mar-06 5:33 

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.