Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Running a Single Instance per User

0.00/5 (No votes)
14 Mar 2006 1  
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:

// 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.

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here