Avoiding Multiple Instances of an Application

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.

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 greatest performance improvement of all is when a system goes from not-working to working.” - John Ousterhout