Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / Windows Forms

Single App Instance in C#: Yet Another Way

Rate me:
Please Sign up or sign in to vote.
2.52/5 (11 votes)
19 Feb 2006CPOL4 min read 81.2K   494   17   17
Explains how to run just one instance of a C# app using Atoms Global table

Introduction

It is a very common situation when for some reason only one instance of an app could be active on a PC. Unfortunately C# (just like many other languages) does not have such a function, so you have to implement it by yourself. I found a few methods on the Internet about how to do this in C#. Some of them are here - on CodeProject. But I would like to introduce one more, which is probably the fastest and easiest, but has some limitations described below.

Background

This method is based on three Windows API functions: checking a unique custom value in Global atom table - should be the very first function of an app; closing Application if such value is in or adding in case the value was not found; finally - it should be removed from the Atoms Table just before this application finished. Global Atom table is a system table that stores strings and corresponding identifiers which are available to all applications. Applications can create two types of atoms: string atoms and integer atoms. This functionality is supported by any Microsoft Windows API since Windows 95 and implemented in KERNEL32.DLL library.

Using the Code

First of all, we have to import three API functions using Platform invoke and the best place to put it is in the main app file - Program.cs:

C#
using System.Runtime.InteropServices;
...
static class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern ushort GlobalAddAtom(string lpString);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern ushort GlobalFindAtom(string lpString);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern ushort GlobalDeleteAtom(ushort atom);

Here is a short description of these functions:

  • GlobalAddAtom: adds a character string to the global atom table and returns a unique value (an atom) identifying the string. lpString should be a string value with maximum size of 255 characters. This parameter could have any value such as a GUID, App path or combination of some Application data such as ProgramName and ProgramVersion. Strings differ only in case that are considered identical. If the function succeeds, the return value is the newly created atom. Read full description on MSDN.
  • GlobalFindAtom: searches the global atom table for the specified character string and retrieves the global atom associated with that string. The parameter should be exactly the same that we are going to use in GlobalAddAtom. If the function succeeds, the return value is the global atom associated with the given string. Read full description on MSDN.
  • GlobalDeleteAtom: removes the Atom from Global Atoms table and decrements the reference count of a global string atom. The function always returns zero. Read full description on MSDN.

Now we can add these functions in the Main() method which normally locates in Program.cs file:

C#
[STAThread]
static void Main()
{
    // Create an unique identifier string:
    // (I'm going to use ProductName and ProductVersion 
    // but it could be, for example, a GUID or 
    // path the Program folder)
    string atomStr = Application.ProductName + Application.ProductVersion;

    // Try to find this Atom in Global Atom table; 
    // if the function will return non-zero positive value 
    // then we have to warn user and close the App:
    ushort Atom = GlobalFindAtom(atomStr);
    if (Atom > 0)
    {
        MessageBox.Show("You are trying to open second instance of the App! 
			This instance will be closed.");
    }
    else
    {
        // The Atom does not exist. Now we need to add it:
        Atom = GlobalAddAtom(atomStr);

        // This code was generated by Visual Studio; keep it as is.
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new mainForm());

        // VERY IMPORTANT:
        // Finally do not forget to remove the Atom before closing the App!!!
        GlobalDeleteAtom(Atom);
    }
}

Good Things and Bad Things

It was easy, wasn't it? But like any solution, this one has good and bad things.

Let's count the good things first:

  • It is easy: just add few lines of code in one place and forget about it!
  • Fast at run-time: Some other methods I saw enum all opened windows or processes to make an attempt to find first an instance of the App which makes the start process slow;
  • Flexible: Since you can define an Atom ID by yourself you can use different IDs, for example, App path to disallow second instance only in case user launches the App from the same location, or ApplicationVersion to allow to run older and newer versions at the same time and disallow to run the same version twice.
  • Give your users choice: You can modify this code to request them to choose: Close the App or allow to raise second instance.

Bad things:

  • Although this method is pretty safe even in case the App will be closed for some reason by system itself or by user using Ctrl-Alt-Del key combination because function GlobalDeleteArom() will do its job anyway, but in case of an unhandled exception the App will be closed immediately and the Atom will stay in Global table which means that user will have to restart his computer to remove the Atom from Atoms table;
  • In case one instance is already opened, this code won't bring the first instance up to user's face but it could be easily fixed using other API functions such as FindWindow() and ShowWindow().

Links

History

  • 19th February, 2006 - Initial revision

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)
Russian Federation Russian Federation
More than 15 years in the industry.
Delphi, C# (Win/WebForms), MS SQL

Comments and Discussions

 
GeneralWhy you dont use this Pin
marcosche25-Jun-09 6:51
marcosche25-Jun-09 6:51 
QuestionWhy would you use Windows atoms to do what mutexes are designed for? Pin
jlchereau14-Nov-08 0:28
jlchereau14-Nov-08 0:28 
AnswerRe: Why would you use Windows atoms to do what mutexes are designed for? Pin
Tinkering Turtle24-Jun-09 4:59
Tinkering Turtle24-Jun-09 4:59 
GeneralThis code doesn't work in some cases!! Pin
fuck bill gates28-Jul-08 4:50
fuck bill gates28-Jul-08 4:50 
General[Message Deleted] Pin
Marc Lievin31-Jul-07 21:39
Marc Lievin31-Jul-07 21:39 
GeneralRe: a small (stupid) question Pin
Vitaly Zayko2-Aug-07 8:30
Vitaly Zayko2-Aug-07 8:30 
General[Message Deleted] Pin
Marc Lievin2-Aug-07 8:45
Marc Lievin2-Aug-07 8:45 
GeneralRe: a small (stupid) question Pin
lievin3-Aug-07 1:33
lievin3-Aug-07 1:33 
QuestionWhat to do for End Task?. Pin
Ashokraj Tirunelveli22-Jan-07 0:58
Ashokraj Tirunelveli22-Jan-07 0:58 
GeneralYet an improvment Pin
Matanel Sindilevich3-Jul-06 3:56
Matanel Sindilevich3-Jul-06 3:56 
Generalhere's a better way Pin
TheGreatAndPowerfulOz20-Feb-06 7:01
TheGreatAndPowerfulOz20-Feb-06 7:01 
GeneralRe: here's a better way Pin
Vitaly Zayko20-Feb-06 9:28
Vitaly Zayko20-Feb-06 9:28 
Probably you are right but CreateMutex has opposite function - ReleaseMutex. What will happen if for some reason an app won't release it before close? I haven't try...
GeneralRe: here's a better way Pin
TheGreatAndPowerfulOz20-Feb-06 11:20
TheGreatAndPowerfulOz20-Feb-06 11:20 
GeneralThe race is on Pin
Grimolfr20-Feb-06 6:41
Grimolfr20-Feb-06 6:41 
GeneralRe: The race is on Pin
Vitaly Zayko20-Feb-06 8:58
Vitaly Zayko20-Feb-06 8:58 
GeneralPassing values to the first instance Pin
loui3119-Feb-06 19:56
loui3119-Feb-06 19:56 
GeneralRe: Passing values to the first instance Pin
Vitaly Zayko19-Feb-06 21:18
Vitaly Zayko19-Feb-06 21:18 

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.