The correct solution
The following code is an adaptation of an example posted in the microsoft.public.mfc
newsgroup by David Lowndes. His example had an option that used FindWindow,
which for reasons I explain below will not work reliably.
Instead, my version uses what I consider a more reliable technique to locate
the window of the other instance. His code also used a shared (inter-process)
variable which introduces another problem which I discuss later.
However, if you deem the problem unimportant, you may choose to use the
simpler version. This solution is the general solution on which the multi-desktop
and multi-user solutions are based; the key difference is in how the unique ID
is formed, a topic discussed later.
In the example below, you must declare the function CMyApp::searcher
as a static class member, for example
static BOOL CALLBACK searcher(HWND hWnd, LPARAM lParam);
You have to declare a registered window message, for example, UWM_ARE_YOU_ME.
For the details of this, see my essay on Message Management.
In the CMainFrame class definition, add a handler for this message:
afx_msg LRESULT OnAreYouMe(WPARAM, LPARAM);
and add a MESSAGE_MAP entry
ON_REGISTERED_MESSAGE(UWM_ARE_YOU_ME, OnAreYouMe)
implement it as follows:
LRESULT CMainFrame::OnAreYouMe(WPARAM, LPARAM)
{
return UWM_ARE_YOU_ME;
} // CMainFrame::OnAreYouMe
Now you can implement the searcher method which looks for the target
window.
BOOL CALLBACK CMyApp::searcher(HWND hWnd, LPARAM lParam)
{
DWORD result;
LRESULT ok = ::SendMessageTimeout(hWnd,
UWM_ARE_YOU_ME,
0, 0,
SMTO_BLOCK |
SMTO_ABORT_IF_HUNG,
200,
&result);
if(ok == 0)
return TRUE; // ignore this and continue
if(result == UWM_ARE_YOU_ME)
{ /* found it */
HWND * target = (HWND *)lParam;
*target = hWnd;
return FALSE; // stop search
} /* found it */
return TRUE; // continue search
} // CMyApp::searcher
//--------------------------------------------------------
BOOL CMyApp::InitInstance()
{
bool AlreadyRunning;
HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE,
_T("MYAPPNAME-088FA840-B10D-11D3-BC36-006067709674"));
// what changes for the alternative solutions
// is the UID in the above call
// which will be replaced by a call on
// createExclusionName
AlreadyRunning = ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED);// The call fails with ERROR_ACCESS_DENIED if the Mutex was
// created in a different users session because of passing
// NULL for the SECURITY_ATTRIBUTES on Mutex creation);if ( AlreadyRunning )
{ /* kill this */
HWND hOther = NULL;
EnumWindows(searcher, (LPARAM)&hOther);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
After you read the section on Race Conditions you may see that this code has
a race condition. If the Mutex was created by instance 1, which is still coming
up (it hasn't created its main window yet), and instance 2 finds that the Mutex
already exists, it tries to find the window for Instance 1. But since Instance
1 is not yet there, this code does not pop up the window for Instance 1. But
that's OK. Because Instance 1 has not yet created its window, it will proceed
to create its window successfully and pop up just like you'd expect.