首页 > 全部文章, 学习笔记 > visual c++入门:mfc窗口的创建和消息响应机制

visual c++入门:mfc窗口的创建和消息响应机制

学了这么长时间的VC++,一直把注意力放在了VC++的核心--MFC这块了,总算用MFC捣弄出来了一个可以响应鼠标动作的小程序,可喜可贺啊,终于有了“实质性”进步了。

简单总结一下mfc窗口的创建和消息响应机制。

在MFC中,有几种典型的窗口对象,CWnd描述的一般窗口对象,CView描述的视图对象,CFrameWnd描述的SDI框窗对象,CMDIFrameWnd描述的MDI框窗对象等等。

在Window下,创建窗口可以使用两个函数,CreateWindow()和CreateWindowEx(),它们都需要一个参数,这个参数是标识窗口类的字符串。所以,如果要创建窗口,一般的做法是,先使用RegisterClass()或RegisterClassEx()注册一个窗口类,然后使用该窗口类来创建窗口。在前面我也提到过,注册窗口类的最主要目的是为系统提供窗口函数的地址,以便被DispatchMessage()之类的函数回。

在MFC中,创建窗口的函数是CWnd或其派生类的Create()或CreateEx方法,注册窗口类一般使用AfxRegisterWndClass(),在这个全局函数中,并没有发现窗口函数地址这样的参数,因此脑子里自然就会有这样的问题:窗口函数在哪里?它是如何同窗口关联的?下面我们将对MFC的一些与此有关的代码进行仔细分析,回答上述两个问题。

窗口函数:在MFC中,有一个全局的函数AfxWndProc(),正如下面的注释所示,它就是CWnd及所有从它派生的窗口类的窗口函数,它的实现如下:

// The WndProc for all CWnd's and derived classes

LRESULT CALLBACK

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

// special message which identifies the window as using AfxWndProc

if (nMsg == WM_QUERYAFXWNDPROC)

return 1

// all other messages route through message map

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd)

ASSERT(pWnd != NULL)

ASSERT(pWnd->m_hWnd == hWnd)

return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam)

}

AfxCallWndProc()调用pWnd对象的虚拟函数WindowProc(),它的代码如下:

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预定义的窗口类,包括同框窗、视图所对应的窗口类。由于它的代码占的篇幅很长,而且实现也很简单,所以就不列出它的代码了,如果你有兴趣,可以在wincore.cpp中找到它的实现代码。

挂接窗口函数

如果你考察过AfxEndDeferRegisterClass()的实现代码,你会对一行代码感到迷惑,下

面列出的是AfxEndDeferRegisterClass()的部分代码,带阴影部分的是那一行令人迷惑的代码:

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)

{

。。。。。。

wndcls.lpfnWndProc = 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

cs.dwExStyle = dwExStyle

cs.lpszClass = lpszClassName

cs.lpszName = lpszWindowName

cs.style = dwStyle

cs.x = x

cs.y = y

cs.cx = nWidth

cs.cy = nHeight

cs.hwndParent = hWndParent

cs.hMenu = nIDorHMenu

cs.hInstance = AfxGetInstanceHandle()

cs.lpCreateParams = lpParam

if (!PreCreateWindow(cs)){

PostNcDestroy()

return FALSE

}

AfxHookWindowCreate(this)

HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,

cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,

cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams)

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 = _afxThreadState.GetData()

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。

小结:在MFC中,创建一个窗口的过程是:

生成一个对应窗口类的对象

调用该对象的Create()或CreateEx()方法

在CreateEx()中(Create()是调用CreateEx()实现的),先调用虚拟函数PreCreateWindow(),让应用程序有一个改变窗口行为的机会,同时,在PreCreateWindow中,还

分类: 全部文章, 学习笔记 标签:
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
click to change 看不清?点击换一张!