Library tutorials & articles
Avoiding Multiple Instances of an Application
Send Message: Race Conditions
One of the most common folkloristic methods (and one I used for years, alas) is to use EnumWindows, do a SendMessage to each window, and look at the return result from SendMessage. What you send is a Registered Window Message (see my essay on Message Management), and if you receive this message you return TRUE. All other windows will not understand this and return FALSE. This turns out to be deeply flawed in a variety of ways.
Note that this method always worked in Win16 because it used cooperative multitasking. It is the preemptive multitasking of Win32 that makes this method fail. And it does.
The SendMessage can hang indefinitely. 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 you haven't gained any reliability.
You can solve this by using SendMessageTimeout. This more-or-less works. You will typically choose a short timeout, for example about 200ms.
But it gets worse.
Microsoft, in violation of all known Windows specifications, has created an application that does not pass messages it doesn't understand to DefWindowProc, which would return 0 for any message it does not understand. Instead, they have a component, apparently associated with Personal Web Server, which has the truly antisocial property of returning 1 for every message sent to its top-level window, whether it understands it or not. So you can't rely on a zero meaning it is not your app.
Well, this could be solved. When I needed to do this for another reason, I ended up having to return the registered window message value, which avoids the Microsoft blunder.
So we've solved the problem of timeouts and bogus messages. We know not to depend on the caption contents, and probably want to avoid worrying about the Window class name. So why doesn't this work?
Because of a much more fundamental problem: a race condition. Just like the one described in the previous section.
The code did an EnumWindows loop and for each HWND it did a SendMessage. So what happened was that application instance 1 searched for another instance of itself, didn't find one, and proceeded to come up. Meanwhile, application instance 2 searched for an instance of itself, but since instance 1 had not yet come up and created its own main window, instance 2 did not find a conflicting instance, and it proceeded to come up. Using a scheme similar to the table I used to demonstrate why the basic FindWindow method doesn't work, you can show that this method will fail for the same reason.
This is the most fundamental failure of this mechanism, and a reason it cannot be used.
Related articles
Related discussion
-
Convert C++ code to VB6
by mawcot (4 replies)
-
How to create a games like FIFA08
by mawcot (0 replies)
-
Binary Studio | software development outsourcing Ukraine
by shane124 (4 replies)
-
Seeking developers for Montreal Office
by mazen_kt (1 replies)
-
Is there anyone here willing to be interviewed regarding their career in IT?
by krizs (1 replies)
I am making a single instance appilcation and have followed all your suggestions . And i am really thankful to you for writing such an informative article.But i have a problem. It is my requirement that user can close my running application from the command prompt.To impliment this i am sending a message (WM_COPYDATA) from new instance which tries to launch but since it finds that an instance is already running it passes the cmdLine parameter from new instance to already existing instance and if the cmdLine parameter is /exit the application is closed. Everything works Like a charm.But problem is this. If i keep the exe in the folder named similar to the window name of my application then the second instance is not able to find the first instance and thus the application does not exit when it should have exited. If i change the name of the folder in which the exe is residing then everything works fine.
I just want to understand why is it happening and would love to have your views on this one.
Brilliant article!! Here it is again, boiled down to cookbook code for your MyApp.cpp file.
Many thanks to Joseph Newcomer & Daniel Lohmann. // Mark Malyj
//Avoiding Multiple Instances of an Application
//Step 1 of 5.
//Include exclusion.h at the top of app.cpp.
include "exclusion.h"
//Step 2 of 5.
//Define UNIQUE_GUID after the includes
//Run Visual Studio tool GUIDGEN.EXE to generate a worldwide-unique GUID for this app;
//choose option 4: registry format, Copy, and define here (DON'T USE this SAMPLE GUID!):
define UNIQUE_GUID _T("{088FA840-B10D-11D3-BC36-006067709674}")
//Step 3 of 5.
//Use a shared variable between all instances of the application.
//This can be done by creating a shared data segment.
//Place this code block following all includes in the App .cpp file
pragma comment(linker, "/SECTION:.shr,RWS")
pragma data_seg(".shr")
HWND g_hWnd = NULL;
pragma data_seg()
//Step 4 of 5.
//Place this code block at the top of App::InitInstance
bool AlreadyRunning;
HANDLE hMutexOneInstance = ::CreateMutex( NULL, TRUE,
//Choose UNIQUETODESKTOP, UNIQUETOSYSTEM,
//UNIQUETOSESSION, or UNIQUETOTRUSTEE here:
createExclusionName(UNIQUEGUID, UNIQUETODESKTOP));
AlreadyRunning = (GetLastError() == ERRORALREADYEXISTS);
if (hMutexOneInstance != NULL)
{
::ReleaseMutex(hMutexOneInstance);
}
if ( AlreadyRunning )
{ /* kill this */
HWND hOther = ghWnd;
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
//Step 5 of 5.
//After Creating the Main Frame, get the handle and store it on the shared variable below.
//Place this code block at the bottom of App::InitInstance, after the mainframe has been created or loaded
ghWnd = mpMainWnd->m_hWnd;
//Remember to add exlusion.h, exclusion.cpp to your project (see the original article)!!
I found this information useful for an implementation that did not involve "Avoiding Multiple Instances of an Application," but did involve use of an EnumWindows() callback loop which was freezing up before all of the windows were enumerated. I was able to find a window that was blocking a call to GetWindowText() (it was busy with an I/O operation). Once I discontinued using GetWindowText(), the enumeration of all windows succeeded. I was then able to find the handle to a specific password prompt (launched by a third-party application) and automatically set the password (as well as post an IDOK message to make the window disapear).
This thread is for discussions of Avoiding Multiple Instances of an Application.