Library tutorials & articles
Avoiding Multiple Instances of an Application
Generalizing the solution for NT
Daniel Lohmann pointed out the fundamental defect of the above mechanism. Although it is reliable, it is not complete, in that it addresses only one of the three possible meanings of "unique instance".
- Avoiding multiple instances started in the same user session.
- Avoiding multiple instances started in the same desktop.
- Avoiding multiple instances started in any session of the same user account.
- Avoiding multiple instances started on the same machine.
In particular, he points out that my way of creating the Mutex name uses a system-global name, guaranteed to be unique for the application, but which is known to all users, sessions, and desktops. Note that this raises the same issue with respect to any use of global names for Mutexes, Semaphores, Events, and even shared memory-mapped files: the assumption that a single name is valid depends upon your interpretation of the above three points. Thus, if you are building a system that uses synchronization primitives and which requires a solution other than (d), you will have to apply the techniques below to the synchronization primitive naming as well.
He points out that in the Terminal Server edition of NT (which is built into Windows 2000), the kernel no longer has a single "global" namespace, but in fact each Terminal Server session has a private namespace. System services share a common namespace for what is called the "console session". He points out that "this all results in consuming much more memory and making some programming tasks quite tricky, but the result is that every user logged into the Terminal Server is able to start its E-Mail client".
There's another little fix he made to my code:
The CreateMutex() call fails with ERROR_ACCESS_DENIED if the Mutex was created in another user's session. This comes from passing NULL for the SECURITY_ATTRIBUTES which results in default security settings. The typical default DACL allows only CREATOR/OWNER and SYSTEM access to the object.
His proposed solution is to extend the name of the Mutex beyond the GUID technique I use, to address the solutions of (a)-(c). He writes:
"I start with (b) because it is simpler. Using GetThreadDesktop() you get a handle to the desktop your thread is running on. Passing this to GetUserObjectInformation(), you get the name of the desktop, which is unique".
"Even (c) is quite easy. The solution is to add the current users account name. Using GetUserName() you get the current users account name. You should qualify it with the current users domain, which can be determined using GetEnvironmentVariable() with USERDOMAIN as variable name."
"For (a) it's a little bit more complicated. You have to open the process token using OpenProcessToken(). Pass this token to GetTokenInformation() to retrieve a TOKEN_STATISTICS structure. The AuthenticationId member of this structure, a 64-bit number (coded as an LUID), contains the unique id of the login session. Convert this into a string".
Based on his description, I created the following subroutine and header file. Note that for any given application, you must decide at compile time which exclusion option you want; for example, if you want to have the application unique to a desktop, choose the UNIQUE_TO_DESKTOP option to generate the key. If you have an application that chooses this dynamically, you can have one running in the system, thinking it is unique, and one running on the desktop, thinking it is unique. I built a little project to test this code, which you can download.
Related articles
Related discussion
-
C++ CATMULL-ROM
by tkruvgt (0 replies)
-
VS2005 app's won't run on another machine
by ted4444 (0 replies)
-
VB.NET: Hide and show table using radio buttons
by converter2009 (1 replies)
-
Convert C++ code to VB6
by mawcot (4 replies)
-
How to create a games like FIFA08
by mawcot (0 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.