1、MFC窗口如何与AfxWndProc建立联系。

当一个新的CWnd派生类创建时,在调用CWnd::CreateEx()过程中,MFC都会安装AfxCbtFilterHook()。这个Hook将拦截HCBT_CREATEWND,将窗体的消息处理函数设置为AfxWndProc()。

  1. // wincore.cpp 651
  2. // CWnd::CreateEx函数通过AfxHookWindowCreate函数安插Hook
  • 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)
  • {
  • // ...
  • // 如果cs中窗体类名为空,PreCreateWindow (见wincore.cpp 714) 将调用
  • // AfxEndDeferRegisterClass (见wincore.cpp 4431)注册默认窗口类,
  • // 默认注册的窗口消息处理函数是DefWindowProc
  • if (!PreCreateWindow(cs))
  • {
  • PostNcDestroy();
  • return FALSE;
  • }
  • // 安插AfxCbtFilterHook
  • AfxHookWindowCreate(this);
  • HWND hWnd = ::AfxCtxCreateWindowEx(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
  • // ...
  • }
  • // wincore.cpp 609
  • void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
  • {
  • _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
  • if (pThreadState->m_pWndInit == pWnd)
  • return;
  • if (pThreadState->m_hHookOldCbtFilter == NULL)
  • {
  • // 安插_AfxCbtFilterHook,因为_AfxCbtFilterHook()是一个基于计算机训练的hook,
  • // 所以windows在激活、创建、销毁、最大化、最小化、移动窗口或改变窗口大小之前,
  • // 会调用_AfxCbtFilterHook()
  • pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
  • _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
  • if (pThreadState->m_hHookOldCbtFilter == NULL)
  • AfxThrowMemoryException();
  • }
  • pThreadState->m_pWndInit = pWnd;
  • }

// wincore.cpp 651
// CWnd::CreateEx函数通过AfxHookWindowCreate函数安插Hook
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)
{
// ...
// 如果cs中窗体类名为空,PreCreateWindow (见wincore.cpp 714) 将调用
// AfxEndDeferRegisterClass (见wincore.cpp 4431)注册默认窗口类,
// 默认注册的窗口消息处理函数是DefWindowProc
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
// 安插AfxCbtFilterHook
AfxHookWindowCreate(this);
HWND hWnd = ::AfxCtxCreateWindowEx(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

// ...
}
// wincore.cpp 609
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (pThreadState->m_pWndInit == pWnd)
return;
if (pThreadState->m_hHookOldCbtFilter == NULL)
{
// 安插_AfxCbtFilterHook,因为_AfxCbtFilterHook()是一个基于计算机训练的hook,
// 所以windows在激活、创建、销毁、最大化、最小化、移动窗口或改变窗口大小之前,
// 会调用_AfxCbtFilterHook()
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
}
pThreadState->m_pWndInit = pWnd;
}

_AfxCbtFilterHook通过SetWindowLongPtr函数将窗口的处理函数替换成AfxWndProc(),同时,在CWnd::m_pfnSuper中保存原来的窗口消息处理函数指针。

  1. // wincore.cpp 472
  2. LRESULT CALLBACK
  3. _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
  4. {
  5. _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
  6. if (code != HCBT_CREATEWND)
  7. {
  8. // 如果不是HCBT_CREATEWND消息,直接忽略。接收到该消息意味着窗口要创建了
  9. return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
  10. wParam, lParam);
  11. }
  12. LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
  13. CWnd* pWndInit = pThreadState->m_pWndInit;
  14. BOOL bContextIsDLL = afxContextIsDLL;
  15. if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL))
  16. {
  17. // ...
  18. HWND hWnd = (HWND)wParam;
  19. WNDPROC oldWndProc;
  20. if (pWndInit != NULL)
  21. {
  22. AFX_MANAGE_STATE(pWndInit->m_pModuleState);
  23. // connect the HWND to pWndInit...
  24. pWndInit->Attach(hWnd);
  25. // allow other subclassing to occur first
  26. pWndInit->PreSubclassWindow();
  27. // 消息处理函数指针,用于存储原来的消息处理函数
  28. WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
  29. // 将窗口的消息处理函数设置成AfxWndProc()
  30. WNDPROC afxWndProc = AfxGetAfxWndProc();
  31. oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
  32. (DWORD_PTR)afxWndProc);
  33. if (oldWndProc != afxWndProc)
  34. *pOldWndProc = oldWndProc;      // 保存原来的指针
  35. pThreadState->m_pWndInit = NULL;
  36. }
  37. // ...
  38. }
  39. // ...
  40. }
  41. // wincore.cpp 1048
  42. WNDPROC* CWnd::GetSuperWndProcAddr()
  43. {
  44. // Note: it is no longer necessary to override GetSuperWndProcAddr
  45. //  for each control class with a different WNDCLASS.
  46. //  This implementation now uses instance data, such that the previous
  47. //  WNDPROC can be anything.
  48. return &m_pfnSuper;
  49. }
  50. // wincore.cpp 392
  51. WNDPROC AFXAPI AfxGetAfxWndProc()
  52. {
  53. // 静态库版本的消息处理函数
  54. return &AfxWndProc;
  55. }

// wincore.cpp 472
LRESULT CALLBACK
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (code != HCBT_CREATEWND)
{
// 如果不是HCBT_CREATEWND消息,直接忽略。接收到该消息意味着窗口要创建了
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
}
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
CWnd* pWndInit = pThreadState->m_pWndInit;
BOOL bContextIsDLL = afxContextIsDLL;
if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL))
{
// ...
HWND hWnd = (HWND)wParam;
WNDPROC oldWndProc;
if (pWndInit != NULL)
{
AFX_MANAGE_STATE(pWndInit->m_pModuleState);
// connect the HWND to pWndInit...
pWndInit->Attach(hWnd);
// allow other subclassing to occur first
pWndInit->PreSubclassWindow();
// 消息处理函数指针,用于存储原来的消息处理函数
WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
// 将窗口的消息处理函数设置成AfxWndProc()
WNDPROC afxWndProc = AfxGetAfxWndProc();
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
(DWORD_PTR)afxWndProc);
if (oldWndProc != afxWndProc)
*pOldWndProc = oldWndProc; // 保存原来的指针
pThreadState->m_pWndInit = NULL;
}
// ...
}
// ...
}
// wincore.cpp 1048
WNDPROC* CWnd::GetSuperWndProcAddr()
{
// Note: it is no longer necessary to override GetSuperWndProcAddr
// for each control class with a different WNDCLASS.
// This implementation now uses instance data, such that the previous
// WNDPROC can be anything.
return &m_pfnSuper;
}
// wincore.cpp 392
WNDPROC AFXAPI AfxGetAfxWndProc()
{
// 静态库版本的消息处理函数
return &AfxWndProc;
}

微软不将AfxWndProc()做为注册窗口过程的原因是DefWindowPorc()可以支持3D控件。这些控件都在微软的CTL3D.dll中。如果系统具有CTL3D功能已经是一种迫切需要,那么应用程序就要覆盖CTL3D的功能(在处理WM_CTLCOLOR消息方面)。为了确保这一点,MFC必须按照以下顺序调用:AfxWndProc()、CTL3D的WndProc()和最后的DefWindowProc()。可见为了确保这一点,微软不得不允许CTL3D在AfxWndProc()之前分类,这就意味着延迟AfxWndProc()的引入。

2、处理消息
MFC用两种方式表示窗口:(1)用统一的系统定义的窗口句柄;(2)用表示窗口的C++类。窗口句柄由CWnd和CWnd的派生类包装。因为窗口句柄是CWnd的成员变量。
MFC用CMapPtrToPtr对象将窗口句柄映射成CWnd对象。MFC在窗口存在期间维护这个链接。如果使用CWnd创建一个窗口,窗口句柄就会和CWnd对象关联在一起,也就是说二者通过句柄映射表关联在一起,MFC这样做就使得框架可以使用C++对象,而不是窗口句柄。
 
AfxWndProc()处理一个特定消息:WM_QUERYAFXWNDPROC,如果消息是WM_QUERYAFXWNDPROC,AfxWndProc()就返回1。应用程序可以通过发送WM_QUERYAFXWNDPROC消息来查询该窗口是否是使用MFC消息映射系统的MFC窗口。
 
  1. // wincore.cpp 375
  2. LRESULT CALLBACK
  3. AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
  4. {
  5. // special message which identifies the window as using AfxWndProc
  6. if (nMsg == WM_QUERYAFXWNDPROC)
  7. return 1;
  8. // all other messages route through message map
  9. CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
  10. if (pWnd == NULL || pWnd->m_hWnd != hWnd)
  11. return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
  12. return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);    // 调用AfxCallWndProc
  13. }
  14. // wincore.cpp 208
  15. LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
  16. WPARAM wParam = 0, LPARAM lParam = 0)
  17. {
  18. _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
  19. MSG oldState = pThreadState->m_lastSentMsg;   // 保存最后发送消息
  20. pThreadState->m_lastSentMsg.hwnd = hWnd;
  21. pThreadState->m_lastSentMsg.message = nMsg;
  22. pThreadState->m_lastSentMsg.wParam = wParam;
  23. pThreadState->m_lastSentMsg.lParam = lParam;
  24. // Catch exceptions thrown outside the scope of a callback
  25. // in debug builds and warn the user.
  26. LRESULT lResult;
  27. #ifndef _AFX_NO_OCC_SUPPORT
  28. // special case for WM_DESTROY
  29. if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
  30. pWnd->m_pCtrlCont->OnUIActivate(NULL);
  31. #endif
  32. // special case for WM_INITDIALOG
  33. CRect rectOld;
  34. DWORD dwStyle = 0;
  35. if (nMsg == WM_INITDIALOG)
  36. _AfxPreInitDialog(pWnd, &rectOld, &dwStyle);        // 使对话框自动置于窗口中间
  37. // 委托给对象的WindowProc,覆盖该函数,可以在MFC查看某个消息之前处理这个消息
  38. lResult = pWnd->WindowProc(nMsg, wParam, lParam);
  39. // more special case for WM_INITDIALOG
  40. if (nMsg == WM_INITDIALOG)
  41. _AfxPostInitDialog(pWnd, rectOld, dwStyle);
  42. pThreadState->m_lastSentMsg = oldState;
  43. return lResult;
  44. }
  45. // wincore.cpp 1737
  46. LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  47. {
  48. // OnWndMsg does most of the work, except for DefWindowProc call
  49. LRESULT lResult = 0;
  50. if (!OnWndMsg(message, wParam, lParam, &lResult))   // wincore.cpp 1746
  51. lResult = DefWindowProc(message, wParam, lParam);
  52. return lResult;
  53. }

// wincore.cpp 375
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);
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); // 调用AfxCallWndProc
}
// wincore.cpp 208
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
MSG oldState = pThreadState->m_lastSentMsg; // 保存最后发送消息
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
// Catch exceptions thrown outside the scope of a callback
// in debug builds and warn the user.
LRESULT lResult;
#ifndef _AFX_NO_OCC_SUPPORT
// special case for WM_DESTROY
if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
pWnd->m_pCtrlCont->OnUIActivate(NULL);
#endif
// special case for WM_INITDIALOG
CRect rectOld;
DWORD dwStyle = 0;
if (nMsg == WM_INITDIALOG)
_AfxPreInitDialog(pWnd, &rectOld, &dwStyle); // 使对话框自动置于窗口中间
// 委托给对象的WindowProc,覆盖该函数,可以在MFC查看某个消息之前处理这个消息
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
// more special case for WM_INITDIALOG
if (nMsg == WM_INITDIALOG)
_AfxPostInitDialog(pWnd, rectOld, dwStyle);
pThreadState->m_lastSentMsg = oldState;
return lResult;
}

// wincore.cpp 1737
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)) // wincore.cpp 1746
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}

OnWndMsg函数很大,它首先过滤出WM_COMMAND、WM_NOTIFY、WM_ACTIVE和WM_SETCURSOR。对于这几个消息,框架有自己的处理方法。如果不是这几个,OnWndMsg会在消息映射表中查找消息。MFC维护着一个消息映射表入口缓存,可以通过散列值访问它

MFC消息处理的更多相关文章

  1. 【转】MFC消息处理(一)

    原文网址:http://blog.csdn.net/hyhnoproblem/article/details/6182120 1.MFC窗口如何与AfxWndProc建立联系. 当一个新的CWnd派生 ...

  2. 深入剖析MFC中对于Windows消息处理、运行机制

    序: 本人对Windows系统.MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的<深入浅出MFC>后,感觉到VISUAL C++的Application ...

  3. MFC下WM_NOTIFY消息处理流程

    参考文章:MFC的消息反射机制 在前一篇文章:MFC消息处理流程概述中描述了MFC消息处理的大体流程.由CWnd::OnWndMsg函数可知,当消息为WM_NOTIFY消息时,调用的是virtual ...

  4. windows消息机制(MFC)

    消息分类与消息队列 Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据 ...

  5. MFC中的几个虚函数

    1.PreTranslateMessage()和WindowProc() PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,通过函数名也可以猜出来.绝 ...

  6. [转]windows消息机制(MFC)

    消息分类与消息队列 Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据 ...

  7. VC2008中如何为MFC应用程序添加和删除消息响应函数

    最近重温<MFC Windows应用程序设计>第二版这本书,里面的代码全部是使用VC6.0写的,我Win7下安装的是VS2008开发环境. VC2008下添加和删除常见的消息响应函数有两种 ...

  8. C语言中的sizeof()

    sizeof,一个其貌不扬的家伙,引无数菜鸟竟折腰,小虾我当初也没少犯迷糊,秉着"辛苦我一个,幸福千万人"的伟大思想,我决定将其尽可能详细的总结一下. 但当我总结的时候才发现,这个 ...

  9. 全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)

    一.前言 编译环境是vs2010(32位). <span style="font-size:18px;">#include<iostream> #inclu ...

随机推荐

  1. go加密算法:非对称加密(一)--RSA

    椭圆曲线加密__http://blog.51cto.com/11821908/2057726 // MyRas.go package main import ( "crypto/rand&q ...

  2. OpenCV-Python 人脸眼睛嘴识别

    # 识别眼睛.嘴巴.人脸 image = cv2.imread('./yong.jpg') gray = cv2.cvtColor(image,code=cv2.COLOR_BGR2BGRA) # 加 ...

  3. 大数据学习--day04(选择结构、循环结构、大数据java基础面试题)

    选择结构.循环结构.大数据java基础面试题 switch: 注意: byte short int char String(jdk1.7支持) 不能是 long float double boolea ...

  4. C语言:一个数组中只有两个数字是出现一次

    //1.一个数组中只有两个数字是出现一次, //其他所有数字都出现了两次. //找出这两个数字,编程实现.a //^=单独两个数的^结果 //单独出现的两个数不同位的标记 //position: ^结 ...

  5. Chip-seq peak annontation

    Chip-seq peak annontation Chip-seq peak annontation PeRl narrowPeak/boardPeak narrowPeak/boardPeak 是 ...

  6. 【8086汇编-Day5】第三次实验

    练习一 结果展示: 这个程序执行下来貌似打印了数字36,但其实是两个数字3.6: 这段就是用来打印3:首先ah里放2,跟后面的int 21h结合起来就是调用21号中断例程的2号子程序,用来打印到标准输 ...

  7. 北京Uber优步司机奖励政策(12月28日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  8. 【转载】四元数-Quaterion

    原文:四元数-Quaterion 四元数(Quaterion)   罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议   ...

  9. 2019年猪年海报PSD模板-第一部分

    14套精美猪年海报,免费猪年海报,下载地址:百度网盘,https://pan.baidu.com/s/1i7bIzPRTX0OMbHFWnqURWQ                        

  10. C++ 基础面试题-1

    请说出下面代码在32位系统下的输出内容 /* ** 2018/03/21 21:43:00 ** Brief: ** Author:ZhangJianWei ** Email:Dream_Dog@16 ...