2024年5月10日发(作者:)
窗口创建及窗口消息映射
D
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
上面的代码中,OnWndMsg()是用来处理该窗口消息的函数,如果某条消息没有
被OnWndMsg()处理,也就是该窗口没有提供处理该消息的函数,它就调用
DefWindowProc()进行处理,DefWindowProc()也是一个虚拟函数,看看它的代
码:
LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if (m_pfnSuper != NULL)
return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
WNDPROC pfnWndProc;
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
else
return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
}
DefWindowProc()的策略很简单,调用基类的窗口函数m_pfnSuper来处理该消
息。
通过上面的分析,可以得出这样的结论:与其说AfxWndProc()是MFC的唯
一窗口函数,还不如说AfxWndProc()是MFC的窗口消息分发中心。正是由于有
了这个消息分发中心,才使得MFC的应用程序能够用有限的几个窗口类,作出
各种形形色色的窗口,使得在应用程序中,增加CWnd的派生类,并不增加系统
中窗口类的个数,将对系统资源的使用控制在一个稳定的范围之内。
注册窗口类除了提供窗口函数外,还指定该窗口的一些外观,如是否有标
题条,窗口缺省背景等等。在MFC框架中,有框窗、视图和控制条(CControlBar)
等,它们除了操作行为不同外,外观等也不相同,所以MFC注册了几种缺省的
窗口类。在MFC中,有一个全局函数AfxEndDeferRegisterClass(LONG
fToRegister),它用来注册MFC预定义的窗口类,包括同框窗、视图所对应的
窗口类。由于它的代码占的篇幅很长,而且实现也很简单,所以就不列出它的
代码了,如果你有兴趣,可以在中找到它的实现代码。
挂接窗口函数
如果你考察过AfxEndDeferRegisterClass()的实现代码,你会对一行代码
感到迷惑,下面列出的是AfxEndDeferRegisterClass()的部分代码,带阴影部
分的是那一行令人迷惑的代码:
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
。。。。。。
dProc = DefWindowProc;
。。。。。。
}
MFC将所有预定义的窗口类的窗口函数都设置成DefWindowProc。大家都知
道,DefWindowProc是Window下为一般窗口提供消息缺省处理的API,它肯定不
是应用程序所需要的窗口函数,所以,MFC肯定在某个地方置换了它,置换
DefWindowProc的代码在哪里呢?
前面说过,在MFC中创建窗口时是用CWnd的两个虚拟函数Create()和
CreateEx(),Create()是通过调用CreateEx()实现的,所以最终窗口的创建都
要归结到CreateEx()函数上。因此,我们可以推断MFC在CreateEx()中置换了
DefWindowProc。为了证实这一点,看看CWnd::CreateEx()的代码:
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
yle = dwExStyle;
ass = lpszClassName;
me = lpszWindowName;
= dwStyle;
cs.x = x;
cs.y = y;
= nWidth;
= nHeight;
rent = hWndParent;
= nIDorHMenu;
nce = AfxGetInstanceHandle();
teParams = lpParam;
if (!PreCreateWindow(cs)){
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(yle, ass,
me, , cs.x, cs.y, , ,
rent, , nce, teParams);
if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if
CreateWindowEx fails too soon
if (hWnd == NULL) return FALSE;
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
return TRUE;
}
从上面的代码看不出任何显式的置换DefWindowProc的代码,其实,它隐藏在
AfxHookWindowCreate(this)之中,顺藤摸瓜,再看看AfxHookWindowCreate()
的代码:
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
_AFX_THREAD_STATE* pThreadState = _a();
if (pThreadState->m_pWndInit == pWnd)
return;
if (pThreadState->m_hHookOldCbtFilter == NULL)
{
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
}
ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);
ASSERT(pWnd != NULL);
ASSERT(pWnd->m_hWnd == NULL); // only do once
ASSERT(pThreadState->m_pWndInit == NULL); // hook not already in
progress
pThreadState->m_pWndInit = pWnd;
}
AfxHookWindowCreate()设置了一个线程级的CBT Hook,该Hook的入口地址为
_AfxCbtFilterHook,_AfxCbtFilterHook是一个全局的MFC函数,
_AfxCbtFilterHook通过调用SetWindowLong()用AfxWndProc()的入口地址置换
掉DefWindowProc。
小结:
1、在MFC中,创建一个窗口的过程是:
1、生成一个对应窗口类的对象
2、调用该对象的Create()或CreateEx()方法
3、在CreateEx()中(Create()是调用CreateEx()实现的),先调用虚
拟函数PreCreateWindow(),让应用程序有一个改变窗口行为的机会,同时,在
PreCreateWindow中,还作了一件很重要的事情,就是如果指向窗口类的字符串
指针为NULL,就调用AfxDeferRegisterClass()注册一个合适的窗口类,将该
注册的窗口类作为创建窗口的类型参数。AfxDeferRegisterClass()就是
AfxEndDeferRegisterClass(),后者根据参数注册相应的窗口类,并将
DefWindowProc作为该窗口类的临时窗口函数。
4、CreateEx()在调用CreateWindowEx()创建真正的窗口对象之前,设
置一个线程级的CBT Hook,该hook在窗口创建完成后被调用,MFC在hook函
数中调用SetWindowLong()将该窗口的窗口函数置换成AfxWndProc。
2、从Window的角度看,任何一个MFC应用程序都只有一个窗口函数
AfxWndProc。
3、AfxWndProc的作用是截获所有的发送给窗口消息,并将这些消息发送给
相应的窗口对象的窗口函数WindowProc处理,所以它实质上是一个窗口消息分
发器,注意,非窗口消息不被AfxWndProc所分发,它们在AfxWndProc被调用
之前就被CWinThread::PreTranslateMessage()处理过了。
4、同PreTranslateMessage()不同的地方在于,AfxWndProc()能够截获所
有的来自于消息队列的和非消息队列的窗口消息(如调用SendMessage()发送的
窗口消息)。
发布评论