• 【转】MFC消息处理(一)


    原文网址:http://blog.csdn.net/hyhnoproblem/article/details/6182120

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

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

    [cpp] view plaincopy
     
    1. // wincore.cpp 651  
    2. // CWnd::CreateEx函数通过AfxHookWindowCreate函数安插Hook  
    3. BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,  
    4.                     LPCTSTR lpszWindowName, DWORD dwStyle,  
    5.                     int x, int y, int nWidth, int nHeight,  
    6.                     HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)  
    7. {  
    8.     // ...  
    9.     // 如果cs中窗体类名为空,PreCreateWindow (见wincore.cpp 714) 将调用  
    10.     // AfxEndDeferRegisterClass (见wincore.cpp 4431)注册默认窗口类,  
    11.     // 默认注册的窗口消息处理函数是DefWindowProc  
    12.     if (!PreCreateWindow(cs))     
    13.     {  
    14.         PostNcDestroy();  
    15.         return FALSE;  
    16.     }  
    17.     // 安插AfxCbtFilterHook  
    18.     AfxHookWindowCreate(this);  
    19.     HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,  
    20.         cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,  
    21.         cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);  
    22.     if (!AfxUnhookWindowCreate())  
    23.         PostNcDestroy();        // cleanup if CreateWindowEx fails too soon  
    24.       
    25.     // ...  
    26. }  
    27. // wincore.cpp 609  
    28. void AFXAPI AfxHookWindowCreate(CWnd* pWnd)  
    29. {  
    30.     _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();  
    31.     if (pThreadState->m_pWndInit == pWnd)  
    32.         return;  
    33.     if (pThreadState->m_hHookOldCbtFilter == NULL)  
    34.     {  
    35.         // 安插_AfxCbtFilterHook,因为_AfxCbtFilterHook()是一个基于计算机训练的hook,  
    36.         // 所以windows在激活、创建、销毁、最大化、最小化、移动窗口或改变窗口大小之前,  
    37.         // 会调用_AfxCbtFilterHook()  
    38.         pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,  
    39.             _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());  
    40.         if (pThreadState->m_hHookOldCbtFilter == NULL)  
    41.             AfxThrowMemoryException();  
    42.     }  
    43.     pThreadState->m_pWndInit = pWnd;  
    44. }  

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

    [cpp] view plaincopy
     
    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. }  

    微软不将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] view plaincopy
     
    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. }  

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

  • 相关阅读:
    《网络是怎样连接的》读书笔记一
    移植mplayer到开发板
    解决ubuntu安装ssh服务无法打开解析包问题
    嵌入式软件工程师面经
    c语言-数组、指针面试题
    Linux命令- echo、grep 、重定向、1>&2、2>&1的介绍
    回调函数的作用
    数据结构-单链表回环函数判断
    算法-一步步教你如何用c语言实现堆排序(非递归)
    算法-快速排序
  • 原文地址:https://www.cnblogs.com/wi100sh/p/4210219.html
Copyright © 2020-2023  润新知