Introduction
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
ERROR_ALREADY_EXISTS
.
- If an application instance is running, find its main window using
FindWindow
or 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);
if (IsWindow(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
BringWindowToTop(hWnd);
SetForegroundWindow(hWnd);
}
}
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 lpszClass
and lpszName
of the CREATESTRUCT
). Another problem is that it is not recommended to use FindWindow
.
Background
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 CreateFileMapping(
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.
-
HANDLE OpenFileMapping(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
Open the File Mapping object with name referred by “lpName” parameter.
-
LPVOID MapViewOfFile(
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.
Reference
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.
Function Reference:
1. CCSingleInstance::CCSingleInstance(LPCTSTR pszUniqueName=NULL,
int nCmdShow=SW_RESTORE)
The construction takes two parameters.
pszUniqueName
is the File Mapping object’s global unique name, if it’s NULL
, CSingleInstance
will use a default name __akui__
.
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
CSingleInstance
object
- 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 SetWindow
in 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.
History
- 29th April, 2009: Initial post