博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MFC消息处理
阅读量:5221 次
发布时间:2019-06-14

本文共 10664 字,大约阅读时间需要 35 分钟。

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

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

 

 

[cpp]
  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中保存原来的窗口消息处理函数指针。

 

[cpp]
  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窗口。
 
[cpp]
  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.   
  46. // wincore.cpp 1737   
  47. LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)  
  48. {  
  49.     // OnWndMsg does most of the work, except for DefWindowProc call   
  50.     LRESULT lResult = 0;  
  51.     if (!OnWndMsg(message, wParam, lParam, &lResult))   // wincore.cpp 1746   
  52.         lResult = DefWindowProc(message, wParam, lParam);  
  53.     return lResult;  
  54. }  

// 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维护着一个消息映射表入口缓存,可以通过散列值访问它

转载于:https://www.cnblogs.com/sideny/p/3281301.html

你可能感兴趣的文章
http站点转https站点教程
查看>>
解决miner.start() 返回null
查看>>
关于MFC中窗口的销毁
查看>>
bzoj 2007: [Noi2010]海拔【最小割+dijskstra】
查看>>
BZOJ 1001--[BeiJing2006]狼抓兔子(最短路&对偶图)
查看>>
C# Dynamic通用反序列化Json类型并遍历属性比较
查看>>
128 Longest Consecutive Sequence 一个无序整数数组中找到最长连续序列
查看>>
定制jackson的自定义序列化(null值的处理)
查看>>
auth模块
查看>>
javascript keycode大全
查看>>
前台freemark获取后台的值
查看>>
log4j.properties的作用
查看>>
游戏偶感
查看>>
Leetcode: Unique Binary Search Trees II
查看>>
C++ FFLIB 之FFDB: 使用 Mysql&Sqlite 实现CRUD
查看>>
Spring-hibernate整合
查看>>
c++ map
查看>>
exit和return的区别
查看>>
ThinkPHP5.1安装
查看>>
js += 含义(小知识)
查看>>