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 [email protected]. 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.
|