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