Avoiding Multiple Instances of an Application

Why FindWindow doesn't work

One of the popular pieces of folklore is to that you can use the FindWindow API call to avoid multiple instances. This API is very dangerous to use. There is an excellent chance that your application will forever hang while executing this call. In any case, even if the conditions that would cause it to hang do not exist, the techniques surrounding its use are so deeply flawed that such a technique cannot possibly work. So it has two failure modes: no instance comes up, or multiple instances come up. Neither of these is terribly satisfactory.

Unfortunately, this folklore is so popular that even Microsoft believes it. In their KB article Q109175, they suggest a download of a program onetime.exe which is a self-extracting zipfile that contains an example that does precisely this. KB Q141752 and its accompanying onet32.exe propagate this myth. Trust me. Been there, done that. This does not work. Do not use this technique.

Why doesn't it work? Well, it works if everything is perfect. But when there is a tiny, minor, little thing wrong, it fails catastrophically, at least as far as the end user is concerned. Furthermore, the thing that is "wrong" does not mean there is a malfunction. A completely correctly-functioning program can cause the failure.

The FindWindow call is:

  HWND CWnd::FindWindow(LPCTSTR classname, LPCTSTR caption)

The classname parameter can either NULL, which means that all windows classes will match, or a window class name. (The underlying API call can also accept an HATOM cast to an LPCTSTR, but it is rare in MFC to have the HATOM from a ::RegisterWindowClass call). The caption parameter is a string which is compared to the caption of each window that is enumerated. The function returns the HWND of the first window whose class name matches classname and whose caption matches caption.

Why won't this work?

Well, unless you have registered the window class of your app using AfxRegisterClass, you don't know the class name. So, you say, "that's OK, I'll just use NULL, but I know the caption name".

You don't.

Well, maybe you do. This week. In this country. For this version. Probably. As long as it isn't an MDI app. 

Well, for one thing, you don't want to hardwire a constant value for the caption value. This would be a colossally losing idea. Because if you internationalize the application, you will change the caption name. And so you lose.

Aha! You will put the caption name in the resource file. Then you'll use LoadString to get it. Well, that's fine, but then you have to make sure that the same LoadString is used to set the caption. OK, maybe this works. For a dialog-based app. But for other apps, the file name of the input file will be incorporated into the caption, and you can't predict what this is, so already FindWindow is useless.

So you can't use FindWindow if you are looking for the caption.

But things get worse. FindWindow really does an EnumWindows call, and for each top-level window handle found, it performs a GetWindowText operation. This is done by sending a WM_GETTEXT message to the window handle. But if the thread that owns the handle is blocked, on a Semaphore, a Mutex, an Event, an I/O operation, or some other manner, the SendMessage will block until that thread frees up and runs. But this may never happen. So FindWindow will block forever, and your application will never come up.

When you have to drive out some considerable distance because your best client can't get the app to start on his machine, you want to make sure that (a) this never happens to him again and (b) it certainly will never happen to any of his customers! It took a long time to find this one.

So, you say, that's clearly a losing idea. I'll use the class name. After all, that's what Microsoft does in their examples, and they Must Know What They Are Doing. 

The code Microsoft supplies as an example is flawed. But the superficial flaws mask the deep flaws. For example, the class name is given as a string name. What is not readily apparent is that you really, really have to modify this name to be unique. Globally unique. As in, no other implementor anywhere else in the known Universe would ever use the same name.

Now here's a little story, which you should all take to heart: a long time ago, I wrote a Win16 application. It registered a window class "generic" because I cloned it from the Microsoft generic example. The complaint was "your program fails". Guess what? It tried to register the window class "generic", which someone else who had cloned their application from the generic example had also used. Back in those days, window class names were systemwide global names, so it failed to register the class. 

Today, you say, those names are not globally unique; it is perfectly valid to have a program A that registers a class "MainWindow" and a program B that registers a class "MainWindow". This is true. But only if neither ever cares about the other. If Program B starts asking what a window's class name is, it can't tell whether the name "MainWindow" is registered by an instance of itself, or by some other program that never heard of it. 

So the first thing to do is make sure your class name is unique, because even though the names are not global, you are about to search for that class name, and you can't tell which of the many registered class instances of the same name you are talking to.

OK, so you have read my other essays and know how to do this by using GUIDGEN. This creates a 128-bit number, expressed as a hex character string, which is known to be globally unique. So you just append it to the human-readable class name. Cool. So you know the name is unique, and you're going to search using that class name. Since you're not falling into the WM_GETTEXT trap, it won't matter if the other task is hung in some way, because after all, it doesn't have to be running for you to get the class information about the window class.

Guess what. You've just fallen into the Race Condition trap. 

Here's a skeleton of the code from one of the Microsoft examples. I've included only the Good Parts.

LPCTSTR lpszUniqueClass = _T("MyNewClass");
//---------------------------------------------------
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    // Use the specific class name we established earlier
	cs.lpszClass = lpszUniqueClass; // [A]

	return CMDIFrameWnd::PreCreateWindow(cs);
}
//----------------------------------------------------------------
BOOL COneT32App::InitInstance()
{
    // If a previous instance of the application is already running,
    // then activate it and return FALSE from InitInstance to end the
    // execution of this instance.
    if(!FirstInstance())// [B]
      return FALSE;

    // Register our unique class name that we wish to use
    WNDCLASS wndcls;

    memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults

    wndcls.style = ...;
    ... other wndcls variable inits here, irrelevant for 
    ... this discussion

    // Specify our own class name for using FindWindow later
    wndcls.lpszClassName = lpszUniqueClass;

    // Register new class and exit if it fails
    if(!AfxRegisterClass(&wndcls)) // [C]
    {
      return FALSE;
    }
 ... rest of InitInstance here...
    CMainFrame* pMainFrame = new CMainFrame; // [D]
 ... more here
 return TRUE;
}
//----------------------------------------------------------------
BOOL COneT32App::FirstInstance()
{
  CWnd *pWndPrev, *pWndChild;
  
  // Determine if another window with our class name exists...
  if (pWndPrev = CWnd::FindWindow(lpszUniqueClass,NULL)) // [E]

//----------------------------------------------------------------

An application will normally initialize by executing the code in such a way that the sequence, as indicated in the // [x] comments (which are alphabetical top-to-bottom, although the code doesn't execute that way) is as follows:

[B]-[E]-[C]-[D]-[A]

Consider the sequence shown below of two applications initializing.

Time Application instance 1 Application instance 2
1 B FirstInstance()  
2 E FindWindow() => FALSE  
3   B FirstInstance()
4   E FindWindow() => FALSE
5 C AfxRegisterClass()  
6   C AfxRegisterClass()
7 D new CMainFrame  
8   D new CMainFrame
9 A (in PreCreateWindow)  
10   A (in PreCreateWindow)

Note that this successfully passes the test! At time 2, when Application instance 1 calls FindWindow, there is no other window instance. So FindWindow returns FALSE, indicating there is no second instance. So the initialization sequence proceeds. But meanwhile Application instance 2 is initializing. At time 4, when it executes FindWindow, there is no other window instance of the desired type. So FindWindow returns FALSE, and Application instance 2 knows it is the only instance. So it continues to initialize. So by the time Application instance 1 creates the window that FindWindow would have found, the test is long since past when Application instance 2 would have found it. And we get two running instances!

If you think this can't happen, you are living in a different world than the real one. I saw it happen. Repeatedly. Approximately one time out of three.

I found this out when a client had a very fast Pentium and had configured the Win98 desktop to launch-on-single-click. Years of conditioning had taught the user to double-click the icon, so whenever he double-clicked the icon, he launched two copies of the app. And far too often, both came up!

So why does the CreateMutex method work? Doesn't it suffer from the same problem? No, because the creation of a Mutex is an atomic operation in the kernel. The creation of a Mutex is guaranteed to complete before any other thread can successfully create a Mutex. Therefore, we are absolutely guaranteed that the creation of the object and the test for its existence are a single operation, not separated by thousands or tens of thousands preemptible instructions as the FindWindow or (as I discuss in the next section) SendMessage methods.

Daniel Lohmann, who has made significant contributions to this article, also points out that in terms of "uniqueness", FindWindow has a problem in that it enumerates only windows in the same desktop as the calling thread. Therefore, if there is another instance running on another desktop you won't find it to pop it up!

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.

“There's no test like production” - Anon