Click here to Skip to main content
Licence CPOL
First Posted 19 Feb 2006
Views 47,638
Downloads 262
Bookmarked 15 times

Single App Instance in C#: Yet Another Way

By | 19 Feb 2006 | Article
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:

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:

[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)

About the Author

Vitaly Zayko

Software Developer (Senior)

Russian Federation Russian Federation

Member

More than 15 years in the industry.
Delphi, C# (Win/WebForms), MS SQL

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralWhy you dont use this Pinmembermarcosche6:51 25 Jun '09  
QuestionWhy would you use Windows atoms to do what mutexes are designed for? Pinmemberjlchereau0:28 14 Nov '08  
AnswerRe: Why would you use Windows atoms to do what mutexes are designed for? PinmemberYeorwned4:59 24 Jun '09  
GeneralThis code doesn't work in some cases!! Pinmemberfuck bill gates4:50 28 Jul '08  
General[Message Deleted] PinmemberMarc Lievin21:39 31 Jul '07  
GeneralRe: a small (stupid) question PinmemberVitaly Zayko8:30 2 Aug '07  
General[Message Deleted] PinmemberMarc Lievin8:45 2 Aug '07  
GeneralRe: a small (stupid) question Pinmemberlievin1:33 3 Aug '07  
QuestionWhat to do for End Task?. Pinmemberashok_tvl0:58 22 Jan '07  
GeneralYet an improvment PinmemberMatanel Sindilevich3:56 3 Jul '06  
Generalhere's a better way Pinmemberahz7:01 20 Feb '06  
GeneralRe: here's a better way PinmemberVitaly Zayko9:28 20 Feb '06  
GeneralRe: here's a better way Pinmemberahz11:20 20 Feb '06  
GeneralThe race is on PinmemberGrimolfr6:41 20 Feb '06  
GeneralRe: The race is on PinmemberVitaly Zayko8:58 20 Feb '06  
GeneralPassing values to the first instance Pinmemberloui3119:56 19 Feb '06  
GeneralRe: Passing values to the first instance PinmemberVitaly Zayko21:18 19 Feb '06  

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

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120517.1 | Last Updated 19 Feb 2006
Article Copyright 2006 by Vitaly Zayko
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid