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.
Comments