Avoiding Multiple Instances of an Application

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.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The generation of random numbers is too important to be left to chance.” - Robert R. Coveyou