Prevent Multiple Win32 Application Instances
How to detect whether a copy of your program is already running
Jeff Prosise

It's an old question, but one that's asked a lot, especially by Windows 3. x programmers making the move to Windows 95 and Windows NT: How do I prevent multiple instances of my applications from running?

In 16-bit Windows, the answer was simple: Check the hPrevInstance parameter passed to WinMain and terminate if hPrevInstance holds a non-NULL value. But in 32-bit Windows, it's not that easy. Because Win32 processes run in separate address spaces, hPrevInstance is always NULL, no matter how many other instances may be running.

That doesn't mean a Win32 application can't detect other instances of itself and prevent further instances from running. In fact, there are several techniques to detect previous instances in Win32. Minor variations on these techniques permit applications to count the number of instances concurrently running (when multiple instances are allowed) and to limit the number of instances permitted.

This article offers Win32 programmers four methods of dealing intelligently with multiple application instances. Sample applications demonstrating all four methods are available for downloading from PC Magazine Online. The file Nomult.zip contains the source code, executables, and Visual C++ 4. x.MAK files for the sample programs, which are called App1, App2, App3, and App4. It also contains the source code and executable for App4Lib.dll, a DLL used by App4. All four sample applications prevent multiple instances of themselves from executing. The technique each application uses is indicated by the number in its name: App1 uses method 1, App2 uses method 2, and so on.


FindWindow() Method

Atom Table Method
Mutex Method
Shared Memor Method
Conclusion

Method 1: Findwindow

One of the easiest ways--and probably the one most commonly used--to detect previous instances of a Win32 application is to use the FindWindow API function to find out whether there is another top-level window created from the same WNDCLASS in the system.

FindWindow accepts two parameters: a pointer to a WNDCLASS name (or optionally, an atom specifying the WNDCLASS name) and a pointer to a window title. It returns the handle of the first top-level window whose WNDCLASS and window title match the parameters specified in the call, or it returns NULL if there is no window with those attributes.

Let's say your application's WinMain function registers a WNDCLASS named MyWindowClass and then calls CreateWindow or CreateWindowEx to create a window from it. To prevent multiple instances of the application from running, simply call FindWindow with a pointer to the string "MyWindowClass" in parameter 1 at the outset of WinMain and return immediately if FindWindow returns a non-NULL window handle.

The WinMain function in Figure 1 illustrates how it's done. If FindWindow reveals that there is another top-level window with the same WNDCLASS, WinMain passes the window handle returned by FindWindow to SetForegroundWindow to bring the previously launched application instance to the foreground. If FindWindow returns NULL, execution continues.

FIGURE 1: This WinMain function uses FindWindow to detect whether other instances of the same application are already running. If another instance is found, the current application instance brings the previous one to the foreground and then terminates.

The FindWindow method works reliably as long as you use WNDCLASS names that are unlikely to be used by other programmers. Don't use a WNDCLASS name that's as generic as MyWindowClass, or your application might very well mistake another completely unrelated application for a previous instance of itself. You can reduce the possibility of this happening by specifying a non-NULL window title in the call to FindWindow, because even if two applications use the same WNDCLASS name, there's still a good chance they will not be using the same window title.

One drawback to FindWindow is that if two instances of the same application are started in rapid succession, there is a slight possibility that neither will detect the other, and as a result both will start normally. Another problem is that some application frameworks, including MFC, register a unique WNDCLASS for every application instance. If you want bulletproof protection against multiple application instances, or if you're an MFC programmer, you should probably consider another method.

Method 2: the Global Atom Table

One method of detecting previous application instances that works equally well with SDK-style and framework-based applications uses the global atom table, a dictionary-like collection of strings maintained by the operating system. Because the table is a systemwide resource, a string added to it by one application can be retrieved by another.

A string added to the atom table is identified by a value of type ATOM. The Win32 API provides functions for adding and deleting strings (GlobalAddAtom and GlobalDeleteAtom), retrieving a string given an ATOM (GlobalGetAtomName), and retrieving an ATOM given a string (GlobalFindAtom).

The WinMain function in Figure 2 shows how to use the global atom table to detect previous application instances. At startup, the application calls GlobalFindAtom to determine whether the string "<MyApp> Amy's birthday is 05-07-93 <MyApp>" has been added to the global atom table. A return value of 0 means that the string does not exist in the atom table and, by inference, that no other instances of the application are running. In that case, WinMain adds the atom to the table itself. Now each time another instance of the application is started while the first is still running, GlobalFindAtom will return a nonzero value, causing the new instance's WinMain to terminate with an error message informing the user that another instance of the application is running. Note that WinMain deletes the atom from the table before the application ends, so the atom won't be left behind. Failure to delete the atom would prevent any future instances of the application from running.

FIGURE 2: This WinMain function prevents multiple application instances by using a global atom as a signal that another instance is already running. This method works equally well with SDK-style and framework-based apps.

To use this method in an MFC application, call the GlobalFindAtom function from the application object's InitInstance function and GlobalDeleteAtom from ExitInstance. The resulting application will behave much like an SDK application that uses Figure 2's WinMain.

Applications that use the FindWindow method to detect previous instances enjoy one advantage over applications that use the global atom table. Since FindWindow returns a window handle, activating a previous instance is a simple matter of passing the window handle to SetForegroundWindow. If you wish to activate a previous application instance detected using the global atom table, you must use FindWindow or a similar method to retrieve the previous instance's window handle.

Method 3: Named Mutexes

In another method of detecting existing Win32 application instances, the first application instance to be launched creates a shared thread-synchronization object or file-mapping object. Subsequent instances can then determine whether another instance is running by checking for the shared object's existence.

The WinMain function in Figure 3 uses a named mutex to detect previous application instances. A mutex (a contraction of the words mut ually and ex clusive) is a synchronization object that is usually used to coordinate the actions of two or more threads. The threads can belong to the same process, or they can belong to different processes; it doesn't matter, because mutexes are kernel objects and thus reach across process boundaries. Two processes that use the same mutex identify the mutex by name.

FIGURE 3: A named mutex can be shared among applications and provides a convenient means for an application to detect another instance of itself.

In Figure 3, the mutex isn't used for synchronization purposes. Instead, it functions as a flag for detecting previous instances. First WinMain calls CreateMutex to create a named mutex. Then it calls GetLastError to determine whether CreateMutex actually created a mutex or whether it simply returned the handle of an existing mutex with the same name. A return value of ERROR_ALREADY_EXISTS means another instance of the application has already created the mutex; that in turn causes WinMain to display an error message and shut down. Any other return value allows startup to proceed normally. Before terminating, WinMain calls CloseHandle to close the mutex handle. When the last instance of the application calls CloseHandle, the mutex's reference count drops to 0 and the system deletes it, freeing the resources allocated to it.

You can use a variation on the named-mutex method to limit the allowed number of instances of your application to a value other than 1. Suppose, for example, you'd like your users to be able to run up to five concurrent instances of your program. Just replace the named mutex with a named semaphore whose initial resource count is 5, then plug the following code into WinMain:

         static char szSemName[] = "MySemaphore";
HANDLE hSem;
.
.
.
// At startup
hSem = CreateSemaphore (NULL, 5, 5,szSemName);
if (WaitForSingleObject (hSem, 0) == WAIT_TIMEOUT)
{
MessageBox (NULL, "There are already five instances of this application
running"
, "Error", MB_OK | MB_ICONSTOP);
CloseHandle (hSem);
return -1;
}
.
.
.
// Before shutdown
ReleaseSemaphore (hSem, 1, NULL);
CloseHandle (hSem);

Semaphores are similar to mutexes, but unlike mutexes, semaphores maintain resource counts that indicate when all the objects they guard are in use. The call to CreateSemaphore creates a named semaphore with an initial resource count of 5 if a semaphore of the same name doesn't already exist; otherwise, it returns the handle of the existing semaphore.

Calling WaitForSingleObject with the semaphore's handle decrements the resource count and returns WAIT_OBJECT_0 if the semaphore's resource count is nonzero. If the resource count is 0, WaitForSingleObject simply returns WAIT_TIMEOUT. Each instance of the application decrements the resource count with WaitForSingleObject when started and increments the resource count with ReleaseSemaphore before terminating. Thus, WaitForSingleObject returns WAIT_OBJECT_0 for the first five application instances. But if a sixth instance is started, WaitForSingleObject returns WAIT_TIMEOUT and the application quits. It's important to pass WaitForSingleObject a time-out value of 0 (parameter 2), or else calling it when the resource count is 0 will block the calling thread until either the time-out period expires or an existing instance of the application is terminated.

Method 4: Shared Memory

Methods 1 through 3 share a common Achilles' heel: If the WNDCLASS name, atom name, or synchronization-object name you choose is not unique, your application might mistake other unrelated running applications for instances of itself. This is unlikely to happen if you choose your names carefully, but it's easier to rest comfortably when you know nothing is left to chance.

The fourth and final method we'll discuss is virtually bulletproof. It relies on shared memory in a DLL that is loaded into the address space of every instance of a given application. Inside that shared memory resides a variable that maintains a running count of the number of instances of the application that are running. The application increments the variable's value (which is initally 0) at startup and decrements it before shutting down. Restricting the number of concurrently running application instances is a simple matter of checking the count at startup and terminating if the count is greater than some predetermined value. The variable can be exported directly, or it can be exposed through exported DLL functions.

The source code listing in Figure 4 shows how this technique translates into code. Figure 4's WinMain function checks the instance count and terminates if it is not 0. The count is retrieved by calling the DLL's GetInstanceCount function. It is incremented with the IncrementInstanceCount function and decremented with DecrementInstanceCount.

FIGURE 4: This code uses an instance count stored in shared memory in a DLL to detect multiple application instances.

The instance count is stored in the DLL in g_lInstanceCount, and g_lInstanceCount is declared between two pragmas that place it in a data segment named Shared. The name alone is not enough to make the data segment a shared segment; you must link the DLL with a

/SECTION:Shared,RWS

switch for the segment to be shared.
What would happen if g_lInstanceCount were not placed in a shared segment? Simple: Every subsequent application instance that links to the DLL would get its own copy of g_lInstanceCount, and GetInstanceCount would always return 0--no matter how many instances were running. Note that g_lInstanceCount is incremented and decremented with the Win32 InterlockedIncrement and InterlockedDecrement functions rather than with the C/C++ increment and decrement operators. This ensures that one thread won't try to increment g_lInstanceCount at the same time another thread tries to decrement it, as might happen when one instance is started at about the same time another instance is terminated.

When you compile and link the DLL, an import library containing references for the three exported functions is generated automatically. If you use the code Figure 4, link your application with the import library, so the .EXE will be able to see the functions in the DLL and the DLL will be loaded automatically when the .EXE is started.

It probably goes without saying, but I'll say it just to be sure: The shared memory technique can be used instead of a named semaphore to limit application instances to a number other than 1. To prevent more than five instances from running at once, you would check the count returned by GetInstanceCount and exit if it equaled 5. You could also modify the DLL to store the window handle of each application instance that has started. Then it would be a simple matter for subsequent instances of the application to activate an already-existing instance before shutting down by retrieving the window handle from the DLL and passing it to SetForegroundWindow.

That's Not All


Are there other methods for detecting multiple application instances in 32-bit Windows? You bet. For example, you could call SendMessage with an HWND_BROADCAST parameter, to broadcast a registered window message to all top-level windows, and then listen for responses. The key, of course, is to provide a handler for the registered window message that allows other running instances of the same application to identify themselves to the instance that sent the message. You could even work it so that the first instance brings itself to the foreground before the second instance shuts down by including a call to SetForegroundWindow in the message handler.

Broadcasting a message and listening for responses sounds a lot like DDE. Save for its complexity, DDE is an ideal mechanism for detecting multiple application instances and passing information between instances. Have you ever wondered how an MDI application written with MFC knows to open a document (and also knows what the document's filename is) when an icon for one of the application's file types is double-clicked? An object created from MFC's CFrameWnd or CMDIFrameWnd class is a DDE server that responds to WM_DDE_EXECUTE messages from the operating system shell by passing the document name referenced in each message to the application object, which in turn opens the document. In MFC 4. x, you can see how it works by looking at the source code files Winfrm.cpp, Appui.cpp, and Docmgr.cpp. The key functions to look at are CFrame Wnd::OnDDEExecute, CWinApp::OnDDECommand, and CDocManager::OnDDECommand. You can adapt the code in these functions to add similar functionality to SDK applications, if you wish.

Are there other Windows programming topics you would like to see addressed in this column? If so, drop me a line in the Power Programming section of the PC MagNet Programming forum on CompuServe. My CompuServe ID is 72241,44. From the Internet, send mail to 72241.44@compuserve.com. N


Jeff Prosise is a contributing editor of PC Magazine. His new book, Programming Windows 95 with MFC, was recently published by Microsoft Press. The first three methods share an Achilles' heel: If you choose a name that's not unique, your app could be confused by instances of another app.