MFC中的消息路由

1.MFC中的消息分爲三種

(1)標準消息,也叫窗口消息(例:WM_PAINT,WM_CREATE,WM_LBUTTONDOWN,WM_CHAR)

(2)命令消息,來自菜單,工具欄和加速鍵,都以WM_COMMAND表示

(3)控件消息,控件消息又分爲三小類,第一類和標準消息格式一樣,第二類和命令消息格式一樣(不過多了一個控件窗口的句柄),第三類是WM_NOTIFY.其具體細節不是本文敘述的重點.

2.爲什麼要消息路由?什麼叫消息路由?

  如果像SDK那樣,我們的程序只有一個窗口,一個窗口函數,那哪還有消息路由呢?所有的消息都有一個窗口函數來處理了。至所以要消息路由,是因爲MFC程序中有CMyView,CMyDoc,CMyFrameWnd,CMyApp等,MFC框架要做的工作是給用戶提供一個機會,讓用戶可以選擇這些類當中的任意一個來處理我們的命令消息。

  注意,消息路由主要是針對上述的第二類消息(命令消息)。對於第一類窗口消息,其消息的接收者是確定的,不需要路由。比如:對於WM_CREATE消息,處理這個消息的類就是產生這個消息的窗口,你不可能讓CMyDoc,CMyFrame,CMyApp去處理CView的WM_CREATE消息,那根本不符合邏輯,MFC框架當然也不會讓你那麼做。而對於來自菜單,工具欄的命令消息,用戶是有選擇權的,用戶可以選擇其接收者爲View,Doc,App,Frame等當中的任意一個。下面就詳細說一下命令消息的路由過程,主要通過分析MFC源代碼。

3.首先說一下函數的調用順序.所有的三類消息初始都被送入一個全局函數AfxWndProc,

之後是pWnd->WindowProc,pWnd->OnWndMsg,在OnWndMsg()中這三類消息分道揚鑣了,其中第一類消息由OnWndMsg自己處理,第二類交給了OnCommand(),第三類交給了OnNotify(),下面主要說第二類的處理過程:

 AfxWndProc()

AfxCallWndProc

pWnd->WindowProc(注意,這裏的pWnd指向的是產生消息的那個窗口,可能是CMyView,CMyFrameWnd等)

pWnd->OnWndMsg()

pWnd->OnCommand()

....

注意:WindowProc和OnWndMsg這兩個函數實際只有CWnd類中才有,在其它類中並沒有重寫這兩個虛函數,所以我們調用的是CWnd::WindowProc()和CWnd::OnWndMsg(),但要注意:它們的this指針是指向我們的程序中的具體類對象(這是下面運用多態的前提)。

下面以多文檔爲例,解析其對第二類消息的處理過程:

對於多文檔來說,命令消息都來自我們自己的主框架窗口(CMyFrameWnd),因爲菜單是屬於主框架窗口。

AfxWndProc-->CWnd::WindProc()-->CWnd::OnWndMsg()(此處三類消息分道揚鑣)-->CMyFrameWnd::OnCommand(不存在)-->CMDIFrameWnd::OnCommand(),

下面是CMDIFrameWnd::OnCommand()的源代碼:

{

CMDIChildWnd* pActiveChild = MDIGetActive();

if (pActiveChild != NULL && AfxCallWndProc(pActiveChild,          (1)

  pActiveChild->m_hWnd, WM_COMMAND, wParam, lParam) != 0)

return TRUE; // handled by child

if (CFrameWnd::OnCommand(wParam, lParam)) (2)

return TRUE; // handled through normal mechanism (MDI child or frame)

.....}

父框架窗口是先認子框架窗口來處理,如果其不處理,再自己處理

  那子框架窗口又是如何處理的呢?由於子框架窗口沒有重寫上面的WindowProc,OnWndMsg,OnCommand,所以最終調用的是其父類CFrameWnd::OnCommand().注意:這裏雖然調用了兩次CFrameWnd::OnCommand,

但其this指針卻不同,一個this指針指向的是CMyChildWnd,一個指向的是CFrameWnd.下面是CFrameWnd::OnCommand()源代碼:

{...

CMyView* pView=GetActiveView()

pView->OnCmdMsg() (1.1)

CWnd::OnCmdMsg(); (1.2)

CMyApp* pApp=(CMyApp*)AfxGetApp(); (1.3)

pApp->OnCmdMsg();

.....}

子框架窗口是先讓CMyView處理,如果其不處理,再自己處理(CWnd::OnCmdMsg),否則,再 App處理.

  那CMyView又是如何處理的呢,下面是其CView::OnCmdMsg()源代碼

{

CWnd::OnCmdMsg(); (1.1.1)

m_pDocument->OnCmdMsg(); (1.1.2)

}

CMyView是先讓自己處理,如果其不處理,再讓其對應的Document處理,而Document是先自己處理,如果自己不處理,現讓CDocTemplate處理(此處這一代碼就不在寫出,因爲通常不會讓文檔模板處理)

  所謂自己處理,就是調用CCmdTarget::OnCmdMsg(),CWnd並沒有重寫這個函數,調用CWnd::OnCmdMsg就是調用CCmdTarget::OnCmdMsg(),這個函數的主要乾的事情就是:調用GetMessageMap()這個虛函數,獲得我們的子類的消息映射表,然後把該消息和此消息映射表對照,看其是否有對應的消息響應函數。如果沒有,再看其父類是否有,父類也沒有,就返回false,讓其它的類來處理了。

通過以上函數調用和返回順序,可以總結出其消息處理順序:首先命令消息來自主框架窗口,它把消息交給了子框架窗口,子框架窗口又交給了View,View自己處理,否則就交給文檔,所以最終的順序是:

CMyView--->CMyDoc-->CMultiDocTemplate(它通常不會處理)-->CMyChildFrame-->CMyApp->CMyFrameWnd

注意:這裏App先於MainFrame,在有些書上說成是App最後(特糾正)

對於單文檔,那就比這簡單了,

CMyView-->CMyDoc-->CSingleDocTemplate-->CMyFrame-->CMyApp

注意:在單文檔中CMyApp是最後,因爲這裏的主框架實際佔據的是MDI中子框架的位置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章