Introduction
In some cases, it is useful to create an MFC application where the main window uses a specific window class.
This is especially useful when I want to provide some easy and simple inter process communication (IPC). The easiest way to communicate from one process to another process is to send a message. The only problem is to determine and identify the target window easily.
One way is to iterate over all top level windows and to determine classname or window name. Or to broadcast a registered windows message to all windows. But this is some overhead and there is an easy way to solve the problem to find a window directly.
Windows provides the function FindWindow, and is easy to find a window with a specific name or class name.
Window titles usually change with the opened file, and might change if the users changes the language for the application (if there is support for multiple languages). But if it is possible to define your own class name, you have something really unique to search for.
But in MFC programs, the class names are internally defined and used by the MFC framework. And for a dialog based application, it is the fixed system class #32770.
Background
The problem is how to change the MFC application to use a class name that is provided by the developer.
The function we need to change is CMainFrame::PreCreateWindow
that is normally already created by the application wizards for MFC SDI and MDI applications.
For a dialog based application, we have to force the system to load our dialog resource template with our defined class name.
Using the Code for SDI and MDI Applications
If you create a SDI/MDI application with the MFC application wizard, you find a function CMainFrame::PreCreateWindow
. This function is called several times and is used to provide the information for the window classes and styles that are used to create the window.
The current code just overrides to normal implementation, registers a new window class with a name provided by us and passes the class name to the resulting structure.
You have to remember that CMainFrame::PreCreateWindow
is called at least twice. Also the MFC routines check the icon that is defined in the window class struct if it matches the one used and defined by CWinApp::LoadFrame
. If the MFC routine (CFrameWnd::GetIconWndClass
) that is calling PreCreateWindow
finds a different hIcon
set in the struct
, it creates its own class using the icon that MFC thinks is the correct one. Usually the icon has the ID IDR_MAINFRAME.
The code below just shows what you have to change in your mainfrm.cpp:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
LPCTSTR pszClassName = _T("OwnClassName");
WNDCLASS wndcls;
if (!::GetClassInfo(AfxGetInstanceHandle(), pszClassName, &wndcls))
{
VERIFY(GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndcls));
wndcls.lpszClassName = pszClassName;
LPCTSTR pszIcon = MAKEINTRESOURCE(IDR_MAINFRAME);
HINSTANCE hInst = AfxFindResourceHandle(pszIcon, ATL_RT_GROUP_ICON);
_ASSERTE(hInst!=NULL);
wndcls.hIcon = ::LoadIcon(hInst, pszIcon);
if (!::RegisterClass(&wndcls))
{
_ASSERTE(!__FUNCTION__ "RegisterClass failed");
return FALSE;
}
}
cs.lpszClass = pszClassName;
return TRUE;
}
Using the Code for Dialog Based Applications
In a dialog based application, we have to do two things.
The class name used for a normal dialog is "#32770"
. When we want to use a different class, we need to tell the dialog template that it should use this special self defined class name, because we don't control the creation of the dialog. There is a property in the resource editor for a dialog to specify a user defined class name but VS-2008 and VS-2010 have a bug here and don't allow the user to enter some data here. The property is grayed and so disabled. Even a changed class name isn't shown in the property window.
But you can change the class name with the editor, and this information is not deleted by the resource editor when we may change the dialog later. So we open the RC file with our preferred editor, search the entry for the dialog template we want to change and add a CLASSNAME
statement as shown here:
IDD_OWNCLASSNAMEDLG_DIALOG DIALOGEX 0, 0, 199, 28
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE |
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "OwnClassNameDlg"
CLASS "OwnClassNameDlg"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
...
The only thing left now is to register a new class based on the system class #32770
with our class name that we set in the dialog template before we call dlg.DoModal();
The code you have to add is shown here and it isn't great magic:
...
WNDCLASS wndcls;
::GetClassInfo(NULL,MAKEINTRESOURCE(32770),&wndcls);
wndcls.lpszClassName = _T("OwnClassNameDlg");
if (!::RegisterClass(&wndcls))
{
_ASSERTE(! __FUNCTION__ " Failed to register window class");
return FALSE;
}
COwnClassNameDlgDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
...
BTW: In fact, "#32770"
is not a class name. It is just a class name registered with an integer ID where MAKEINTRESOURCE(32770)
is used as a class name. It is also ok to use the string "#32770"
.
Have fun...
History
- 10 May, 2011 -- Version 1.0