为 MFC 应用程序提供自定义类名






4.76/5 (20投票s)
为 MFC 应用程序 SDI/MDI 和基于对话框的应用程序提供自定义类名

引言
在某些情况下,创建主窗口使用特定窗口类的 MFC 应用程序非常有用。
当我想提供一些简单易懂的进程间通信 (IPC) 时,这尤其有用。从一个进程到另一个进程进行通信的最简单方法是发送消息。唯一的问题是轻松确定和识别目标窗口。
一种方法是遍历所有顶级窗口并确定类名或窗口名称。或者向所有窗口广播已注册的 Windows 消息。但这会带来一些开销,并且有一种简单的方法可以直接查找窗口来解决该问题。
Windows 提供了 FindWindow 函数,可以很容易地找到具有特定名称或类名的窗口。
窗口标题通常会随着打开的文件而更改,如果用户更改应用程序的语言(如果支持多种语言),也可能会更改。但是,如果可以定义自己的类名,那么您就拥有了真正独特的搜索对象。
但是在 MFC 程序中,类名是由 MFC 框架在内部定义和使用的。 对于基于对话框的应用程序,它是固定的系统类 #32770。
背景
问题是如何更改 MFC 应用程序以使用由开发人员提供的类名。
我们需要更改的函数是 CMainFrame::PreCreateWindow
,它通常已由 MFC SDI 和 MDI 应用程序的应用程序向导创建。
对于基于对话框的应用程序,我们必须强制系统使用我们定义的类名加载我们的对话框资源模板。
使用 SDI 和 MDI 应用程序的代码
如果使用 MFC 应用程序向导创建 SDI/MDI 应用程序,您会找到一个函数 CMainFrame::PreCreateWindow
。此函数被多次调用,用于提供用于创建窗口的窗口类和样式的信息。
当前代码只是覆盖为正常实现,注册一个由我们提供名称的新窗口类,并将类名传递给结果结构。
您必须记住,CMainFrame::PreCreateWindow
至少被调用两次。此外,MFC 例程会检查窗口类结构中定义的图标是否与 CWinApp::LoadFrame
使用和定义的图标匹配。如果调用 PreCreateWindow
的 MFC 例程 (CFrameWnd::GetIconWndClass
) 在 struct
中找到不同的 hIcon
设置,它将使用 MFC 认为正确的图标创建自己的类。通常,该图标的 ID 为 IDR_MAINFRAME
。
下面的代码只是显示了您必须在 *mainfrm.cpp* 中更改的内容
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// Just clear the styles we don't want.
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
// Check if our class is already defined
LPCTSTR pszClassName = _T("OwnClassName");
WNDCLASS wndcls;
if (!::GetClassInfo(AfxGetInstanceHandle(), pszClassName, &wndcls))
{
// Get the current requested window class
VERIFY(GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndcls));
// We want to register this info with our name
wndcls.lpszClassName = pszClassName;
// Need to preset the icon otherwise the function GetIconWndClass
// calling us will overwrite our class.
LPCTSTR pszIcon = MAKEINTRESOURCE(IDR_MAINFRAME);
HINSTANCE hInst = AfxFindResourceHandle(pszIcon, ATL_RT_GROUP_ICON);
_ASSERTE(hInst!=NULL);
wndcls.hIcon = ::LoadIcon(hInst, pszIcon);
// Register our class now and check the outcome
if (!::RegisterClass(&wndcls))
{
_ASSERTE(!__FUNCTION__ "RegisterClass failed");
return FALSE;
}
}
// Now use our class
cs.lpszClass = pszClassName;
return TRUE;
}
使用基于对话框的应用程序的代码
在基于对话框的应用程序中,我们需要做两件事。
用于普通对话框的类名是 "#32770"
。当我们想要使用不同的类时,我们需要告诉对话框模板,它应该使用这个特殊的自定义类名,因为我们不控制对话框的创建。资源编辑器中有一个属性可以为对话框指定用户定义的类名,但是 VS-2008 和 VS-2010 在这里有一个错误,不允许用户在这里输入一些数据。该属性是灰色的,因此被禁用。即使更改后的类名也不会显示在属性窗口中。
但是您可以使用编辑器更改类名,并且当我们稍后可能更改对话框时,此信息不会被资源编辑器删除。因此,我们使用我们喜欢的编辑器打开 RC 文件,搜索我们要更改的对话框模板的条目,并添加一个 CLASSNAME
语句,如此处所示
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
...
现在剩下的唯一事情是,在我们调用 dlg.DoModal();
之前,基于系统类 #32770
注册一个新类,并使用我们在对话框模板中设置的类名。
您必须添加的代码如下所示,它并没有什么了不起的魔法
...
// Just get default class for the dialogs
WNDCLASS wndcls;
::GetClassInfo(NULL,MAKEINTRESOURCE(32770),&wndcls);
// Set our own class name
wndcls.lpszClassName = _T("OwnClassNameDlg");
// Just register the class
if (!::RegisterClass(&wndcls))
{
_ASSERTE(! __FUNCTION__ " Failed to register window class");
return FALSE;
}
COwnClassNameDlgDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
...
顺便说一句:事实上,"#32770"
不是一个类名。它只是一个使用整数 ID 注册的类名,其中 MAKEINTRESOURCE(32770)
用作类名。也可以使用字符串 "#32770"
。
玩得开心...
历史
- 2011 年 5 月 10 日 -- 版本 1.0