The common way of creating a “single instance application” (only one instance is allowed to be running at a time) is made by the following two steps:
- Create a named global object (such as Mutex, Semaphore) when the application starts. If an instance is already started, it will fail with the error code
- If an application instance is running, find its main window using
FindWindowEx, then restore and bring the window to the front.
The code is much like this:
HANDLE hMutex = CreateMutex(NULL, FALSE, MUTEX_NAME);
if (GetLastError() == ERROR_ALREADY_EXISTS)
HWND hWnd = FindWindowEx(NULL, NULL, CLASSNAME, NULL);
I seek a new implementation because I don't quite like the previous commonly used method. To Find the running instance’s main window, it must be created with a known class name and (or) window name, yet it’s quite awkward in MFC to do this (we must override the main windows’
PreCreateWindow function and set
lpszName of the
CREATESTRUCT). Another problem is that it is not recommended to use
We can combine the two steps with “File Mapping” technique. File Mapping can be used to share data between processes. When the first application instance starts, it creates a named File Mapping object based on paging file (disk file used as system virtual memory), and stores its main window handle in the 4 bytes mapped share memory. The latter instance finds and opens the named File Mapping object, and retrieves the running main window handle in the mapped share memory.
File Mapping related functions are listed here:
HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName );
Creates a File Mapping object with a system global name. The
hFile parameter can be an opened disk file handle or
(HANDLE)-1 to refer to the paging file.
lpName parameter is a global name of the object and is used to find the File Mapping object created by another process.
DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName );
Open the File Mapping object with name referred by “lpName” parameter.
HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap );
Maps a view of file into address space of the calling process. The returned value is a
VOID pointer, it points to a disk file’s offset address. The disk file can be a real file or the paging file.
I wrote a class
CSingleInstance to wrap the functions and ease its using. The whole implementation contains only one source file “SingleInstance.h”. Only two lines of source code are needed to use it.
1. CCSingleInstance::CCSingleInstance(LPCTSTR pszUniqueName=NULL,
The construction takes two parameters.
pszUniqueName is the File Mapping object’s global unique name, if it’s
CSingleInstance will use a default name
nCmdShow tells how the running instance’s main window is shown when activated.
2. void CCSingleInstance::SetWindow(HWND hWnd)
Using the Code
- Include “SingleInstance.h”
- Declare a global
- After the main window handle is valid, call
SetWindow to store it
In an MFC application, just declare the
CSingleInstance object in AppName.cpp, before declaration of
theApp. And call
CAppName::InitInstance() just before return when main window handle is valid.
If you want to make some changes to the source code, pay attention to the fact that a global object is created before the application actually begins running (enters
WinMain). So don't call MFC functions because MFC may not have been properly initialized yet.
- 29th April, 2009: Initial post