7. How do I ensure that only one instance of my program runs?
Take a look at this code. The first piece of code would be
called very early in your program, typically inside InitInstance for an MFC program.
The unique name here is created with a little help from GUIDGEN.EXE, which is
part of the platform SDK.
if (WeAreAlone
("Unique_8487B3D3-4623-4551-8ACA-EDE405FDA836"))
{
// Proceed
}
else
DebugMessage ("Error: app already running!");
BOOL CMyApp::WeAreAlone (LPSTR szName)
{
HANDLE hMutex = CreateMutex (NULL, TRUE, szName);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hMutex);
return FALSE;
}
return TRUE;
}
Why go to this complexity ? You may well ask. Some
people just check for a fixed window class, indeed Microsofts own sample code does exactly
this. The problem is that if your application is started up programmatically, the second
instance may be running before the first instance has managed to create its window, and
the window-class-based method will not work under those circumstances.
The code above will work fine if you're only worried about instances of your
program running under a single user account. But what if there may be instances
running under another account, such as one running under SYSTEM (a.k.a.
LocalSystem) ? The code above will fail in a subtle way, because one process doesn't have
access rights to the others mutex. You can assume an ERROR_ACCESS_DENIED means
this situation pertains or, more properly, you can construct the mutex with an
appropriate security descriptor in the first place. The code below demonstrates
this:
static
SECURITY_ATTRIBUTES g_sa = {0};
static HANDLE g_hsa
= 0;
//---------------------------------------------------------------------------
BOOL IsExistingInstanceOf (LPCSTR szName)
{
if (OpenMutex
(MUTEX_ALL_ACCESS, FALSE, szName) == NULL)
{
CreateNullDaclSd ();
CreateMutex (&g_sa, FALSE, szName);
FreeNullDaclSd ();
return
FALSE ;
}
else
return TRUE ;
}
//---------------------------------------------------------------------------
// This creates a NULL-DACL-SD (a real SD, but with an all-access DACL).
// Passing NULL for a security descriptor under NT5 gets you an SD
// which has only CREATOR/OWNER and SYSTEM access.
//
// The method below gets everyone access to the object, which is what I
// want for cross-account objects like exclusion mutexes (which can be
// created by current user or the LOCALSYSTEM account but must be visible
// to both).
BOOL CreateNullDaclSd ()
{
BOOL bRetVal = FALSE;
g_sa.nLength = sizeof(g_sa);
g_hsa = GlobalAlloc (GHND,SECURITY_DESCRIPTOR_MIN_LENGTH);
g_sa.lpSecurityDescriptor = GlobalLock(g_hsa);
g_sa.bInheritHandle = TRUE;
if (InitializeSecurityDescriptor
(g_sa.lpSecurityDescriptor, 1))
{
if
(SetSecurityDescriptorDacl (g_sa.lpSecurityDescriptor,
TRUE,
NULL,
FALSE))
{
bRetVal = TRUE;
}
else
{
OutputDebugString ("CNDS:
cannot set security descriptor DACL\n");
}
}
else
{
OutputDebugString("CNDS:
cannot initialise security descriptor\n");
}
return
bRetVal;
}
//---------------------------------------------------------------------------
void FreeNullDaclSd
()
{
GlobalUnlock (g_hsa);
GlobalFree (g_hsa);
}
//---------------------------------------------------------------------------
If you have to deal with multiple desktops, or terminal server, then you're
outside the scope of this article and I refer you to Joe Newcomers very thorough
article on this topic, here