Introduction
This article describes six methods which can be used to limit the number of running instances of a given application. It starts with describing an old method which was used in the Win16-era and not useful today. That is why that method has ‘zero’ as its name. The others show different principles of achieving the main goal.
METHOD 0: Does Nothing Nowadays
The entry point for both Windows-based and Win32-based applications is WinMain
(or wWinMain
). This function has four parameters, two of which are of the type HINSTANCE
. The first one represents the instance of a given application and the second one represents the instance of a previous application running, if any. This second parameter is NULL
in Win32 hence it does nothing. In Win16, one of its usages was determining the existence of a previous instance of a given application. One might use it as follows:
int WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow
)
{
if (hPrevInstance != NULL)
{
return(0);
}
}
This method is totally obsolete. Several methods that may be used in Win32 are presented below.
METHOD 1: Mutex as a Named Object
Mutexes may be created as named or unnamed objects depending on the last parameter of the CreateMutex
function. If taking a null string, it creates an unnamed mutex, otherwise it creates a named mutex. This named mutex is able to be recognized by another application, of course, by its name. So, named mutexes can be used to limit the number of instances running of a given application. The following code is to be placed in the WinMain
function (or in the InitInstance
method if MFC is on):
HANDLE hMutex = CreateMutex(NULL, FALSE,
"{FA531CC1-0497-11d3-A180-00105A276C3E}");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
return 0;
}
CloseHandle(hMutex);
Usually, a name is passed as a GUID to guarantee its uniqueness. But you may name a mutex whatever you desire.
Mutexes are not the only objects that are able to carry names. There are other synchronization objects that can be used instead. Nevertheless, mutexes’ case is a classical representation.
The next method is also a named object’s one. It uses a memory-mapped file as a named object. The main difference between the map-files’ method and the mutexes’ method is that the latter cannot be used in Win16, but the former can. Perhaps, because of that, the map-files’ method is shown separately.
METHOD 2: Memory-mapped File as a Named Object
In the WinMain
function (or in the InitInstance
method if MFC is on), we can put the following code which demonstrates how to use a named map-file to limit the number of instances running of a given application:
HANDLE hMap = CreateFileMapping( (HANDLE) 0xffffffff,
NULL,
PAGE_READONLY,
0,
32,
"{FA531CC1-0497-11d3-A180-00105A276C3E}" );
if(hMap)
{
if( GetLastError() == ERROR_ALREADY_EXISTS )
{
return 0;
}
}
else
{
return 1;
}
All methods shown so far have a restriction; they allow only one instance to be run. Theoretically, it is possible to limit the number of instances of a given application by two or more instances. The next three methods show how to do it. Besides, they may be considered alternatives to the previous ones.
METHOD 3: Shared Sections
A nice method is to make use of shared sections to store a global variable that can be recognized by all instances of a given application. The following macros are to be placed before the WinMain
function’s definition (or the InitInstance
method if MFC is on):
#pragma data_seg("MyShared")
volatile UINT g_nInstancesRunning = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:MyShared,RWS")
The code above declares a shared section named ‘MyShared
’ and puts a global variable in it of type UINT
. The keyword ‘volatile
’ tells the compiler not to try optimizing the variable’s behavior. The ‘comment
’ macro tells the linker to prepare the ‘MyShared
’ section to be Readable, Writable and Shared. Now, in the WinMain
(or InitInstance
), we may check for the variable's value. For this purpose, we would like to use the InterlockedExchangeAdd
function; this function atomically adds a LONG
value to its first parameter and returns its previous value. For example, if we want to set a limit number of 5, the following code will do:
if (5 == ::InterlockedExchangeAdd((PLONG) &g_nInstancesRunning, 1))
{
return FALSE;
}
But, it is necessary to reduce the value of the variable before WinMain
exits (ExitInstance
if MFC is on):
::InterlockedExchangeAdd((PLONG) &g_nInstancesRunning, -1);
METHOD 4: One Mutex per Instance
Why not use mutexes to set a limit number to not only 1, but also 2 or more? In this case, a nice algorithm can be developed to help. The essence of this method is to prepare one named mutex per instance running until the maximum number reaches. I’ve built a C++ class, CInstanceLimit
, to encapsulate the code. Simply declare an object globally and use it in the WinMain
function (or InitInstance
if MFC is on). By the way, the destructor will free all resources captured; you need not worry about it. The constructor takes two parameters; the first is the maximum number of instances allowed to run (the default is 1), and the second is a unique string, mostly a GUID (the default is the name of the class). For example, we may use it like so:
CInstanceLimit g_limit(5);
if (g_limit.Limit())
{
return FALSE;
}
The source code of this class is to be downloaded.
METHOD 5: Registry
Another idea is to use the Registry to store the current number of instances running. Every time an instance is getting run, the value in the Registry must be incremented, and before exiting, the same value in the Registry must be decremented. This technique is demonstrated by a C++ class, CInstanceRegLimit
. This class is similar to the previous method’s class. But this one is used only by MFC applications, as it uses MFC’s services to access the Registry. To use the class, we need to declare its object as a member-variable of the CWinApp
derived class in order to make the object’s destructor get called before ‘theApp
’ is destroyed to update the Registry correctly. Then, in the InitInstance
, we have:
SetRegistryKey(_T("My Company Name"));
if (m_limit.Limit())
{
return FALSE;
}
Note, the ‘if
’ instruction must be placed after the call to the SetRegistryKey
to ensure that the data will be stored in the Registry rather than in an INI file. Of course, if you want to use INI files, simply remove the SetRegistryKey
call.
The source code of this class is to be downloaded.