Shared Variable: A different problem
Another method which has been proposed is to use a shared variable between
all instances of the application. This can be done by creating a shared data
segment. The technique is of the form:
#pragma comment(linker, "/SECTION:.shr,RWS")
#pragma data_seg(".shr")
HWND hGlobal = NULL;
#pragma data_seg()
// in the startup code:
// g_hWnd is set when the main window is created.
BOOL CMyApp::InitInstance()
{
bool AlreadyRunning;
HANDLE hMutexOneInstance = ::CreateMutex( NULL, TRUE,
_T("MYAPPNAME-088FA840-B10D-11D3-BC36-006067709674"));
AlreadyRunning = (GetLastError() == ERROR_ALREADY_EXISTS);
if (hMutexOneInstance != NULL)
{
::ReleaseMutex(hMutexOneInstance);
}
if ( AlreadyRunning )
{ /* kill this */
HWND hOther = g_hWnd;
if (hOther != NULL)
{ /* pop up */
::SetForegroundWindow(hOther);
if (IsIconic(hOther))
{ /* restore */
::ShowWindow(hOther, SW_RESTORE);
} /* restore */
} /* pop up */
return FALSE; // terminates the creation
} /* kill this */
// ... continue with InitInstance
return TRUE;
} // CMyApp::InitInstance
This almost works. It avoids the fundamental race condition, because the CreateMutex
call is an atomic operation. No matter what the relative timings of the two processes
are, exactly one of them will create the Mutex first, and the other will get
the ERROR_ALREADY_EXISTS. Note that I used GUIDGEN to get a guaranteed-unique
ID.
The use of the shared variable presents a problem. This shared variable is
only shared with other instances from the same executable. This means that if
you run a version of the debug executable, and a version of the release executable,
one cannot find the other's window to pop it up. Thus, when an instance finds
that it is a duplicate (they still share the same Mutex name), it cannot find
its other instance to pop it up. This will confuse you.
The code, however, is simpler than mine; it doesn't need the EnumWindows
handler, or the code inside it, or a user-defined Registered Window Message,
or a handler in CMainFrame.
I don't understand why the Mutex is created in owned mode (the second parameter
is TRUE). The Microsoft documentation even says that when doing a CreateMutex
from separate threads that this parameter must always be FALSE because
otherwise it is impossible to determine which thread actually owns the Mutex.
Since this Mutex is not used in any way in the code, the use of the TRUE
parameter doesn't seem to have any value.
Daniel Lohmann has observed that shared memory is shared even if the processes
run under different user accounts, as long as the instances are on the same machine.
Of course it's also shared if the instances reside on different desktops. Therefore,
the use of shared variables has marginal value--and may even be harmful--when
you generalize the notion as he indicates in his suggestions in the next section.