TN001:窗口类注册
TN001: Window Class Registration
本文解析了微软Windows所需要的WNDCLASS在MFC下的注册方法。特别是讨论了MFC和Windows所使用的WNDCLASS的属性。
问题的提出
CWnd对象的属性,与Windows里的HWND一样,存储在两个地方:窗口对象及WNDCLASS中。WNDCLASS不同于C++里的类。WNDCLASS的名字被传递为窗口创建函数,如CWnd::Create和CFrameWnd::Create中,的lpszClassName参数。
WNDCLASS必须以以下四种之一的方式注册:
l 由MFC提供的WNDCLASS隐式注册
l 由子类化Windows(或其它控件)控件隐式注册
l 由调用MFC的AfxRegisterWndClass或AfxRegisterClass函数显式注册
l 由调用Windows例程RegisterClass显式注册
MFC与WNDCLASS
WNDCLASS结构体里的变量域描述了一个窗口类,下表解析了它们在MFC应用程序里的意义。
Style |
Style of window: see below |
LpfnWndProc |
窗口过程,必须为AfxWndProc |
CbClsExtra |
未使用(值为0) |
CbWndExtra |
未使用(值为0) |
HInstance |
由AfxGetInstanceHandle自动填充 |
HIcon |
框架窗口的图标,参见下面内容 |
HCursor |
鼠标在窗口内的形状,参见下面内容 |
HbrBackground |
背景颜色,参见下面内容 |
LpszMenuName |
未使用(值应为NULL) |
LpszClassName |
窗口类名,参见下面内容 |
提供的WNDCLASS
在先前的MFC版本(4.0以前)中,预先提供了一些窗口类的定义。这些窗口类不再默认提供了,因为这牵涉到版本技术问题(MFC的多个版本加载到同样的内存空间),以及考虑到MFC应用程序和OLE控件都有可能使用MFC DLL的因素。
可参考以下内容将之前提供的WNDCLASS代码移植到新版本中。应用程序应使用AfcRegisterWndClass(使用合适的参数)来替代这些类。
以下为这些窗口类及其属性:
l AfxWnd用于CWnd::Crate创建的所有子窗口
n 窗口类样式:CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW
n 无图标
n 箭头鼠标
n 无背景色
l AfxFrameOrView用于框架窗口或视图(包括独立的CFrameWnd及CMDIChildWnd)
n 窗口类样式:CS_DBLCLKS(为在重新配置大小的时候减少闪烁)
n 图标:AFX_IDI_STD_MDIFRAME
n 箭头鼠标
n 无背景色
l AfxControlBar标准控件条的实现
n 窗口类样式:0(在重新配置大小的时候减少闪烁,无鼠标双击)
n 无图标
n 箭头鼠标
n 灰色背景色(COLOR_BTNFACE)
若应用程序提供了带有ID(如AFX_IDI_STD_FRAME)的资源,MFC会使用此资源,否则使用默认的资源。例如图标使用标准的应用程序图标,鼠标为标准的箭头鼠标。
每个MDI应用程序的单个文档有两个图标(一个为主应用程序,另一个为document/MDIChild窗口图标)。对于使用不同图标的多文档类型,必须注册额外的WNDCLASS,或使用CFrameWnd::LoadFrame函数。
CFrameWnd::LoadFrame会自动注册WNDCLASS,并使用标准的AfxFrameOrView属性,但使用程序员指定的图标ID,作为LoadFrame的第一个参数。
MDIFrameWnd的背景颜色及鼠标值无效,因为MDIFrameWnd的客户区完全被MDICLIENT窗口覆盖了。微软并不鼓励子类化MDICLIENT窗口,因此请尽可能使用标准的颜色及鼠标。
子类化控件
若子类化或父类化一个Windows控件(如CButton),那么此类将自动获得由此控件的Windows实现提供的WNDCLASS属性。
AfxRegisterWndClass函数
MFC给注册窗口类提供了一个帮助例程。给定一组属性(窗口类样式,鼠标,背景刷,图标),产生一个组合的名称,并注册生成的窗口类。例如:
const char* AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor,
HBRUSH hbrBackground, HICON hIcon);
此函数返回表示产生并注册的窗口类的名字的临时字符串。请参见Class Library Reference以获得更多信息。
返回的字符串是一个指向静态字符串缓冲区的指针,并且在下一次调用AfxRegisterWndClass之前都是有效的。若需要保存此字符串,可以将其存储在一CString变量中。例如:
CString strWndClass = AfxRegisterWndClass(CS_DBLCLK, ...);
...
CWnd* pWnd = new CWnd;
pWnd->Create(strWndClass, ...);
...
AfxRegisterWndClass在窗口类注册失败时将会抛出CResourceException异常(这有可能是由不正确的参数,或Windows内存溢出造成的)。
RegisterClass和AfxRegisterClass函数
如果希望使用比AfxRegisterWndClass提供的更成熟的功能,可以调用Windows API函数RegisterClass,或MFC函数AfxRegisterClass。CWnd,CFrameWnd及CMDIChildWnd的Create函数将一个窗口类的名称lpszClassName