消息的傳遞與發送是Windows應用程序的核心所在,任何事件的觸發與響應均要通過消息的作用才能得以完成。在SDK編程中,對消息的獲取與分發主要是通過消息循環來完成的,而在MFC編程中則是通過采取消息映射的方式對其進行處理的。相比而言,這樣的處理方式要簡單許多,這也是符合面向對象編程中盡可能隱含實現細節的原則。
壹個完整的MFC消息映射包括對消息處理函數的原型聲明、實現以及存在於消息映射中的消息入口。這幾部分分別存在與類的頭文件和實現文件中。壹般情況下除了對自定義消息的響應外,對於標準Windows 消息的映射處理可以借助ClassWizard向導來完成。
在選定了待處理的Windows 消息後,向導將會根據消息的不同而生成具有相應函數參數和返回值的消息處理代碼框架。下面這段代碼給出了壹個完成的MFC消息映射過程:
// 在.h文件中的聲明
//{{AFX_MSG(CMessageMapView)
afx_msg void OnMove(int x, int y);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
……
// 在.cpp文件中的實現
BEGIN_MESSAGE_MAP(CMessageMapView, CView)
//{{AFX_MSG_MAP(CMessageMapView)
ON_WM_MOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
……
void CMessageMapView::OnMove(int x, int y)
{
CView::OnMove(x, y);
// TODO: Add your message handler code here
}
這裏對Windows標準消息WM_MOVE做了消息映射,其中用到的BEGIN_MESSAGE_MAP、END_MESSAGE_MAP和頭文件中的DECLARE_MESSAGE_MAP等均是用於消息映射的宏。這些宏聲明了在應用程序框架中可用於在系統中瀏覽所有對象映射的成員變量和函數。除了以上三個比較常見的宏之外,MFC還提供了其他壹些用於消息映射的宏,詳情可參見下表:
宏名 說明
DECLARE_MESSAGE_MAP 在頭文件聲明源文件中所含有的消息映射
BEGIN_MESSAGE_MAP 標記源文件消息映射的開始
END_MESSAGE_MAP 標記源文件消息映射的結束
ON_COMMAND 將特定命令的處理委派給類的壹個成員函數
ON_CONTROL 映射壹個函數到壹個定制控制通知消息。其中,定制控制通知消息是從壹個控制發送到其父窗口的消息。
ON_CONTROL_RANGE 將壹個控制ID的範圍映射到壹個消息處理函數
ON_CONTROL_REFLECT 映射壹個由父窗口反射回控制的通知消息
ON_MESSAGE 將壹個用戶自定義消息映射到壹消息處理函數
ON_NOTIFY 映射壹個控制消息到壹個函數
ON_NOTIFY_RANGE 映射壹個控制ID範圍內的控制消息到壹個函數
ON_NOTIFY_EX 映射壹個控制消息到壹個函數,該成員函數返回FALSE或TRUE來表明通知是否應被傳送到下壹個對象以進行其他反應。
ON_NOTIFY_EX_RANGE 映射壹個控制ID範圍內的控制消息到壹個函數,該成員函數返回FALSE或TRUE來表明通知是否應被傳送到下壹個對象以進行其他反應
ON_NOTIFY_REFLECT 映射壹個控制消息到壹個函數。該消息將會被控制的父窗口反射回來。
ON_REGISTERED_MESSAGE 映射壹個唯壹的消息到壹個將要處理該註冊消息的函數上。該消息是由RegisterWindowMessage()函數註冊的。
ON_UPDATE_COMMAND_UI 映射壹個函數來處理壹個用戶接口更新命令消息
ON_UPDATE_COMMAND_UI_RANGE 映射壹個命令ID的範圍到壹個更新消息處理函數
壹般作為基類使用的CWnd類為Windows消息定義了大量窗口消息的缺省處理函數,這些函數大部分只是簡單地調用了Windows的缺省過程,可以在派生類中對其進行重載。但是MFC應用程序框架卻並沒有象使用普通虛函數那樣使用Windows消息處理函數,而是通過宏將指定的消息映射到派生類的成員函數。如果MFC仍象普通虛函數壹樣對消息響應函數進行處理,那麽CWnd類就要為這上百個消息聲明虛函數。而C++將為在程序中使用的每壹個派生類都提供壹個被稱作vtable的虛擬函數分配表,這個分配表需要為每壹個虛函數提供壹個4字節的入口,而不管這些函數在派生類中是否真正被重載,這將不能有效利用存儲空間。而且對於每壹個不同類型的窗口或控件,應用程序都要為其提供壹個超過400字節的虛擬函數分配表來實現對消息的響應。而采用MFC的用宏將Windows消息映射到C++成員函數的方式則可避免產生龐大的虛擬函數分配表,其消耗的內存是同它所包含的消息入口數量成正比的。
消息映射的工作原理
前面給出了消息映射的壹般形式,下面就對消息映射的工作原理做更深入的分析。任何使用了MFC應用程序框架的Windows程序都含有壹個從CWinApp派生的應用程序類對象,成員函數Run()將被隱含調用,其調用的CWinThread類成員函數Run()將通過對GetMessage()、TranslateMessage()和DispatchMessage()等函數的調用完成同WinMain()類似的消息循環。在消息處理中,幾乎所有的窗口對象都使用AfxWndProc()窗口處理函數,並通過壹個包含了窗口句柄和對象指針等信息的列表而獲取到壹個指向對象的指針,由此可以調用CWnd的虛函數WindowProc()。WindowProc()函數調用了CWnd的另壹個成員函數OnWndMsg(),該函數首先檢查到達的究竟是消息,命令還是通知(Notify),如果是消息就通過消息映射宏DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP 完成對消息的映射。在宏定義中封裝了部分代碼,這些被封裝的預定義代碼可以在VC安裝目錄下的