八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第115部分

深入浅出MFC第2版(PDF格式)-第115部分

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






                                                                                                                         559 


…………………………………………………………Page 622……………………………………………………………

             第篇    深入  MFC  程式設計 



              要知道,虚拟函数必须经由一个虚拟函数表(virtual function table ,vtable )实作出来,每 



              一个子类别必须有它自己的虚拟函数表,其内至少有父类别之虚拟函数表的内容复本(请 



              参考第2章「类别与对象大解剖」一节)。好哇,虚拟函数表中的每一个项目都是一个 



              函数指针,价值4 字节,如果基础类别的虚拟函数表有100 个项目,经过10 层继承, 



              开枝散叶,总共需耗费多少内存在其中?最终,系统会被巨大的额外负担(overhead ) 



              拖垮! 



              这就是为什么MFC 采用独特的消息映射机制而不采用虚拟函数的原因。 



        米诺托斯 (Minotauros)与西修斯 (Theseus) 



             截至目前我还有一些细节没有交待清楚,像是消息的比对动作、消息处理例程的调用动 



             作、以及参数的传递等等,但至少现在可以先继续进行下去,我的目标瞄准消息唧筒(叫 



             邦浦也可以啦)。 



             窗口接收消息后,是谁把消息唧进消息映射网中?是谁决定消息该直直往父映射表走 



             去?还是拐向另一条路(请回头看看图9…2 )?消息的绕行路线,以及MFC  的消息唧 



             筒的设计,活像是米诺托斯的迷宫。不过别担心,我将扮演西修斯,让你免遭毒手。 



             米诺托斯(Minotauros ),希腊神话里牛头人身的怪兽,为克里特岛国王迈诺斯之妻所生。 



             迈诺斯造迷宫将米诺托斯藏于其中,每有人误入迷宫即遭吞噬。怪兽后为雅典王子西修 



             斯(Theseus )所杀。 



             MFC 2。5 (注意,是2。5 而非4。x)曾经在WinMain 的第一个重要动作AfxWinInit 



             之中, 自动为程序注册四个Windows 窗口类别, 并且把窗口函数一致设为 



             AfxWndProc : 



560 


…………………………………………………………Page 623……………………………………………………………

                                                       第9章   訊息映射與命令繞行   



//in APPINIT。CPP (          ) 

                    MFC 2。5 

BOOL AFXAPI AfxWinInit (HINSTANCE hInstance; HINSTANCE hPrevInstance; 

                       LPSTR lpCmdLine; int nCmdShow) 

{ 

     。。。 

     // register basic WndClasses  (以下开始注册窗口类别) 

     WNDCLASS wndcls; 

     wndcls。lpfnWndProc = AfxWndProc; 



     // Child windows no brush; no icon; safest default class styles 

     。。。 

     wndcls。lpszClassName = _afxWnd; 

   if (! ::RegisterClass(&wndcls)) 

             return FALSE; 



     // Control bar windows 

     。。。 

     wndcls。lpszClassName = _afxWndControlBar; 

   if (! ::RegisterClass(&wndcls)) 

             return FALSE; 



     // MDI Frame window (also used for splitter window) 

     。。。 

  if (!RegisterWithIcon (&wndcls; _afxWndMDIFrame; AFX_IDI_STD_MDIFRAME)) 

             return FALSE; 



     // SDI Frame or MDI Child windows or views normal colors 

     。。。 

  if (!RegisterWithIcon (&wndcls; _afxWndFrameOrView; AFX_IDI_STD_FRAME)) 

             return FALSE; 

     。。。 

} 



下面是AfxWndProc  的内容: 



// in WINCORE。CPP (          ) 

                     MFC 2。5 

LRESULT CALLBACK AFX_EXPORT 

AfxWndProc (HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam) 

{ 

  CWnd* pWnd; 



  pWnd = CWnd::FromHandlePermanent(hWnd); 

  ASSERT(pWnd != NULL); 

  ASSERT(pWnd…》m_hWnd == hWnd); 



                                                                                      561 


…………………………………………………………Page 624……………………………………………………………

                   第篇    深入  MFC  程式設計 



                     LRESULT lResult = _AfxCallWndProc (pWnd; hWnd; message; wParam; lParam); 

                     return lResult; 

                   } 



                   // Official way to send message to a CWnd 

                   LRESULT PASCAL _AfxCallWndProc (CWnd* pWnd; HWND hWnd; UINT message; 

                                                  WPARAM wParam; LPARAM lParam) 

                   { 

                     LRESULT lResult; 

                     。。。 

                     TRY 

                     { 

                        。。。 

                        lResult = pWnd…》WindowProc (message; wParam; lParam); 

                     } 

                     。。。 

                     return lResult; 

                   } 



                  MFC 2。5  的CWinApp::Run 调用PumpMessage ,后者又调用::DispatchMessage ,把消息源 



                  源推往AfxWndProc    (如上),最后流向pWnd…》WindowProc  去。拿SDK 程序的本质来 



                  做比对,这样的逻辑十分容易明白。 



                  MFC 4。x 仍旧使用AfxWndProc  作为消息唧筒的起点,但其间却隐藏了许多关节。 



                  但愿你记忆犹新,第6章曾经说过,MFC 4。x 适时地为我们注册Windows 窗口类别(在 



                  第一次产生该种型式之窗口之前)。这些个Windows 窗口类别的窗口函数各是「窗口所 



                  对应之C++ 类别中的DefWindowProc 成员函数」,请参考第 6章 「CFrameWnd::Create 



                  产生主窗口」一节。这就和MFC 2。5  的作法(所有窗口类别共享同一个窗口函数)有了 



                  明显的差异。那么,推动消息的心脏,也就是CWinThread::PumpMessage  中调用 



                  的::DispatchMessage (请参考第 6章 「CWinApp::Run 程序生命的活水源头」一节), 



                  照说应该把消息唧到对应之C++ 类别的DefWindowProc 成员函数去。但是,我们发现 



                  MFC 4。x  中仍然保有和MFC 2。5 相同的AfxWndProc ,仍然保有AfxCallWndProc ,而且 



                  它们扮演的角色也没有变。 



562 


…………………………………………………………Page 625……………………………………………………………

                                                   第9章   訊息映射與命令繞行   



事实上,MFC 4。x  利用hook ,把看似无关的动作全牵联起来了。所谓hook ,是Windows 



程序设计中的一种高阶技术。通常消息都是停留在消息队列中等待被所隶属之窗口抓 



取,如果你设立hook ,就可以更早一步抓取消息,并且可以抓取不属于你的消息,送往 



你设定的一个所谓「滤网函数(filter )」。 



请查阅Win32 API 手册中有关于SetWindowsHook 和SetWindowsHookEx 两函数,以获 



得更多的hook 信息。(可参考Windows 95           A Developer s Guide 一书第6章Hooks ) 



MFC 4。x  的hook 动作是在每一个CWnd 衍生类别之对象产生之际发生,步骤如下: 



// in WINCORE。CPP (MFC 4。x) 



// 请回顾第6章「CFrameWnd::Create产生主窗口」一节 



BOOL CWnd::CreateEx(。。。) 

 { 

     。。。 

     PreCreateWindow(cs);   // 第6章曾经详细讨论过此一函数。 

     AfxHookWindowCreate(this); 

     HWND hWnd = ::CreateWindowEx(。。。); 

     。。。 

 } 



                  (        ) 

 // in WINCORE。CPP  MFC 4。x 

void AFXAPI AfxHookWindowCreate(CWnd* pWnd) 

 { 

    。。。 

    pThreadState…》m_hHookOldCbtFilter =  ::SetWindowsHookEx(WH_CBT; 

            _AfxCbtFilterHook; NULL; ::GetCurrentThreadId()); 

    。。。 

 } 



 WH_CBT 是众多hook 类型中的一种,意味着安装一个puter…Based Training  (CBT ) 



 滤网函数。安装之后,Windows 系统在进行以下任何一个动作之前,会先调用你的滤网 



 函数: 



令一个窗口成为作用中的窗口(HCBT_ACTIVATE ) 



产生或摧毁一个窗口(HCBT_CREATEWND 、HCBT_DESTROYWND ) 



最大化或最小化一个窗口(HCBT_MINMAX ) 



                                                                                563 


…………………………………………………………Page 626……………………………………………………………

                    第篇    深入  MFC  程式設計 



                      搬移或缩放一个窗口(HCBT_MOVESIZE ) 



                      完成一个来自系统菜单的系统命令(HCBT_SYSTEMMAND ) 



                      从系统队列中移去一个鼠标或键盘消息 (HCBT_KEYSKIPPED  、 



                       HCBT_CLICKSKIPPED ) 



                    因此,经过上述hook 安装之后,任何窗口即将产生之前,滤网函数_AfxCbtFilterHook  一 



                    定会先被调用: 



                    _AfxCbtFilterHook(int code; WPARAM wParam; LPARAM lParam) 

                     { 

                        _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); 

                        if (code != HCBT_CREATEWND) 

                        { 

                                // wait for HCBT_CREATEWND just pass others on。。。 

                                return CallNextHookEx(pThreadState…》m_hHookOldCbtFilter; code; 

                                        wParam; lParam); 

                        } 

                        。。。 

                                if (!afxData。bWin31) 

                                { 

                                        // perform subclassing right away on Win32 

                                        _AfxStandardSubclass((HWND)wParam); 

                                } 

                                else 

                                { 

                                   。。。 

                                } 

                        。。。 

                        LRESULT lResult = CallNextHookEx(pThreadState…》m_hHookOldCbtFilter; code; 

                                wParam; lParam); 

                        return lResult; 

                     } 



                    void AFXAPI _AfxStandardSubclass(HWND hWnd) 

                     { 

                        。。。 

                        // subclass the window with standard AfxWndProc 

                        oldWndProc = (WNDPROC)SetWindowLong (hWnd; GWL_WNDPROC; 

                                (DWORD)AfxGetAfxWndProc ()); 

                     } 



                    WNDPROC AFXAPI AfxGetAfxWndProc() 

                     { 



564 


…………………………………………………………Page 627……………………………………………………………

                                                         第9章   訊息映射與命令繞行   



    。。。 

    return &AfxWndProc; 

} 



啊,非常明显,上面的函数合力做了偷天换日的勾当:把「窗口所属之Windows 窗口类 



别」中所记录的窗口函数,改换为AfxWndProc 。于是,::DispatchMessage 就把消息源源 



推往AfxWndProc  去了。 



这种看起来很迂回又怪

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的