Virtual Developer Workshop: Containerized Development with Docker
There are two new paragraphs
Making single-instance an application means enabling
the application to recognize, at startup, if another running
instance of itself exists so that, in this case, the application
stops its execution. Generally, before quitting, the new instance
retrieves the main window handle of the existing instance via the
FindWindow() or FindWindowEx() function and uses this handle to
bring the application window to foreground.
The problem can be solved in several ways. The most known solutions are:
- Using a CMutex (a synchronization object global to the system).
- Using shared data sections.
The small class described in this article, CSingleInstanceApp, uses the first method to implement the single-instance behaviour in a MFC application. It makes very simple to add the feature to a new project, eliminating the necessity of rewriting a consistent amount of code.
Comparison with other similar classes
In this section, I summarize the main features that distinguish my class from other similar classes you can find in this site. I hope this will help the reader to decide which code suits his needs better.
- CSingleInstanceApp is an extension of CWinApp. This reduces the work needed to add the single-instance behaviour.
- It uses a safe and stable method to build the class name, avoiding conflicts with other running applications.
For details about how to use a CMutex to implement the
single-instance you can read the article "Single Instance of
an application Class" by Kevin Lussier. In this article,
I will discuss only the method I have used to build the window
class name string. I assume that you know what a window class is
and the reason why Windows requires the class registration before
creating a window.
The key point to implement single-instance is that we need the registered class name to be the same in every instance of the application. Moreover, it is very important that two different applications don't use the same class name. The framework uses AfxRegisterWndClass() to register the window class but, unfortunately, this function registers a class name that is different in different instances of the same application (you can see it using the GetClassName() function). Thus, we cannot pass this string to FindWindow(). But what is the class name registered by AfxRegisterWndClass()?
To answer this question, once again, we have to browse in the MFC source code. We discover an interesting thing: AfxRegisterWndClass() generates a name for the class with the following code:
// generate a synthetic name for this class HINSTANCE hInst = AfxGetInstanceHandle(); if (hCursor == NULL && hbrBackground == NULL && hIcon == NULL) wsprintf(lpszName, _T("Afx:%x:%x"), (UINT)hInst, nClassStyle); else wsprintf(lpszName, _T("Afx:%x:%x:%x:%x:%x"), (UINT)hInst, nClassStyle, (UINT)hCursor, (UINT)hbrBackground, (UINT)hIcon);
Well, the idea behind the CSingleInstanceApp class is to mimic this way to generate the name string. hInst (the application instance handle), nClassStyle (the window class style), hCursor (the window cursor handle) and hbrBackground (the window background brush handle) are the same in every instance of the application. Only hIcon changes, so simply we cut it off from the class name string. Moreover, we have to make the class name application-dependent by adding to it the application name returned by the AfxGetAppName() function. This is exactly what the CheckSingleInstance() member function does. This is an excerpt from CheckSingleInstance():
if (hCursor == NULL && hbrBackground == NULL && hIcon == NULL) m_strClassName.Format(_T("%s:%x:%x"), lpstrAppName, (UINT)hInst, nClassStyle); else m_strClassName.Format(_T("%s:%x:%x:%x:%x"), lpstrAppName, (UINT)hInst, nClassStyle, (UINT)hCursor, (UINT)hbrBackground);
How to use CSingleInstanceApp
Using this class requires the following steps:
- Include the "CSingleInstanceApp.c" and ".h" in your project.
- At the beginning of the application class header file #include "SingleInstanceApp.h". In the class declaration replace CWinApp with CSingleInstanceApp.
- Add the following as the first line of the InitInstance() body of your application class:
if (!CheckSingleInstance(IDR_MAINFRAME)) return FALSE;
- Add the following as the first line of the PreCreateWindow() body of your frame window class:
cs.lpszClass = ((CMyApp*)AfxGetApp())->GetClassName();
Operations for CSingleInstanceApp
BOOL CheckSingleInstance(UINT nID); CString GetClassName() const;
The previous version of the class contained a bug: the main window produced an horrible flickering when trying to move or resize it. This is due to the fact that the window class style used the CS_HREDRAW and the CS_VREDRAW styles. Moreover, the background brush handle must be NULL, since for the mainframes, the background is automatically painted.
Lots of persons noted that the application can't open another document besides the first by double-clicking its icon. This is not due to a CSingleInstanceApp bug.
Briefly, the DDE (Dynamic Data Exchange can be defined as a
protocol to implement a simple form of inter-process
communication). The MFC application is perfectly capable to
recognize and handle the DDE command to open a document.
This feature is enabled in the InitInstance() body of your application class with the line
However, to enable this characteristic, you need to register the document type "manually" (unfortunately, the framework doesn't do it). You can do this from any explorer window, by selecting "Folder options" from the "View" menu. Go to the "File types" section and select your document type. Now press the "Edit..." button. When the modify dialog appears press the new "Edit..." button. Select the "Use DDE" checkbox. In the "DDE Message" edit box write the following string:
Now, disable the following line in the InitInstance() body of your application.
RegisterShellFileTypes() // Disable this line to receive DDE messages
Now your application is able to open documents with a double click even while it's running.
I'm continuously working to improve this class. I'll be grateful to you if you mail me your comments, advice, or bug apparition reports!
Updated: May, 27 1998