?? mfc
字號:
<P></P>
<P
align=justify>如前所述,消息映射數組的元素是消息映射條目,條目的格式符合結構AFX_MESSAGE_ENTRY的描述。所以,要初始化消息映射數組,就必須使用符合該格式的數據來填充:如果指定當前類處理某個消息,則把和該消息有關的信息(四個)和消息處理函數的地址及原型組合成為一個消息映射條目,加入到消息映射數組中。</P>
<P
align=justify>顯然,這是一個繁瑣的工作。為了簡化操作,MFC根據消息的不同和消息處理方式的不同,把消息映射劃分成若干類別,每一類的消息映射至少有一個共性:消息處理函數的原型相同。對每一類消息映射,MFC定義了一個宏來簡化初始化消息數組的工作。例如,前文提到的ON_COMMAND宏用來映射命令消息,只要指定命令ID和消息處理函數即可,因為對這類命令消息映射條目,其他四個屬性都是固定的。ON_COMMAND宏的初始化內容如下:</P>
<P align=justify>{WM_COMMAND,</P>
<P align=justify>CN_COMMAND,</P>
<P align=justify>(WORD)ID_APP_ABOUT,</P>
<P align=justify>(WORD)ID_APP_ABOUT,</P>
<P align=justify>AfxSig_vv,</P>
<P align=justify>(AFX_PMSG)OnAppAbout</P>
<P align=justify>}</P>
<P
align=justify>這個消息映射條目的含義是:消息ID是ID_APP_ABOUT,OnAppAbout被轉換成AFX_PMSG指針類型,AfxSig_vv是MFC預定義的枚舉變量,用來標識OnAppAbout的函數類型為參數空(Void)、返回空(Void)。</P>
<P align=justify>在消息映射數組的最后,是宏END_MESSAGE_MAP的內容,它標識消息處理類的消息映射條目的終止。</P>
<P align=justify></P>
<LI>對messageMap的初始化
<P></P>
<P align=justify>如前所述,messageMap的類型是AFX_MESSMAP。</P>
<P align=justify>經過初始化,域lpEntries保存了消息映射數組_messageEntries的地址;如果動態鏈接到MFC
DLL,則pfnGetBaseMap保存了_GetBaseMessageMap成員函數的地址;否則pBaseMap保存了基類的消息映射數組的地址。</P>
<P align=justify></P>
<LI>對函數的實現
<P></P></LI></OL>
<P align=justify>_GetBaseMessageMap()</P>
<P align=justify>它返回基類的成員變量messagMap(當使用MFC DLL時),使用該函數得到基類消息映射入口表。</P>
<P align=justify></P>
<P align=justify>GetMessageMap():</P>
<P align=justify>它返回成員變量messageMap,使用該函數得到自身消息映射入口表。</P>
<P align=justify></P>
<P align=justify>順便說一下,消息映射類的基類CCmdTarget也實現了上述和消息映射相關的函數,不過,它的消息映射數組是空的。</P>
<P align=justify></P>
<P align=justify>既然消息映射宏方便了消息映射的實現,那么有必要詳細的討論消息映射宏。下一節,介紹消息映射宏的分類、用法和用途。</P>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889013></A><A name=_Toc445782416></A><A
name=_Toc452640904></A><A name=_Toc457298972></A><B>消息映射宏的種類</B>
<P></P></LI></OL></OL></OL>
<P
align=justify>為了簡化程序員的工作,MFC定義了一系列的消息映射宏和像AfxSig_vv這樣的枚舉變量,以及標準消息處理函數,并且具體地實現這些函數。這里主要討論消息映射宏,常用的分為以下幾類。</P>
<OL>
<P align=justify>
<LI>用于Windows消息的宏,前綴為“ON_WM_”。
<P></P>
<P
align=justify>這樣的宏不帶參數,因為它對應的消息和消息處理函數的函數名稱、函數原型是確定的。MFC提供了這類消息處理函數的定義和缺省實現。每個這樣的宏處理不同的Windows消息。</P>
<P
align=justify>例如:宏ON_WM_CREATE()把消息WM_CREATE映射到OnCreate函數,消息映射條目的第一個成員nMessage指定為要處理的Windows消息的ID,第二個成員nCode指定為0。</P>
<P align=justify></P>
<LI>用于命令消息的宏ON_COMMAND
<P></P></LI></OL>
<P
align=justify>這類宏帶有參數,需要通過參數指定命令ID和消息處理函數。這些消息都映射到WM_COMMAND上,也就是將消息映射條目的第一個成員nMessage指定為WM_COMMAND,第二個成員nCode指定為CN_COMMAND(即0)。消息處理函數的原型是void
(void),不帶參數,不返回值。</P>
<P
align=justify>除了單條命令消息的映射,還有把一定范圍的命令消息映射到一個消息處理函數的映射宏ON_COMMAND_RANGE。這類宏帶有參數,需要指定命令ID的范圍和消息處理函數。這些消息都映射到WM_COMMAND上,也就是將消息映射條目的第一個成員nMessage指定為WM_COMMAND,第二個成員nCode指定為CN_COMMAND(即0),第三個成員nID和第四個成員nLastID指定了映射消息的起止范圍。消息處理函數的原型是void
(UINT),有一個UINT類型的參數,表示要處理的命令消息ID,不返回值。</P>
<P align=justify>(3)用于控制通知消息的宏</P>
<P
align=justify>這類宏可能帶有三個參數,如ON_CONTROL,就需要指定控制窗口ID,通知碼和消息處理函數;也可能帶有兩個參數,如具體處理特定通知消息的宏ON_BN_CLICKED、ON_LBN_DBLCLK、ON_CBN_EDITCHANGE等,需要指定控制窗口ID和消息處理函數。</P>
<P
align=justify>控制通知消息也被映射到WM_COMMAND上,也就是將消息映射條目的第一個成員的nMessage指定為WM_COMMAND,但是第二個成員nCode是特定的通知碼,第三個成員nID是控制子窗口的ID,第四個成員nLastID等于第三個成員的值。消息處理函數的原型是void
(void),沒有參數,不返回值。</P>
<P
align=justify>還有一類宏處理通知消息ON_NOTIFY,它類似于ON_CONTROL,但是控制通知消息被映射到WM_NOTIFY。消息映射條目的第一個成員的nMessage被指定為WM_NOTIFY,第二個成員nCode是特定的通知碼,第三個成員nID是控制子窗口的ID,第四個成員nLastID等于第三個成員的值。消息處理函數的原型是void
(NMHDR*, LRESULT*),參數1是NMHDR指針,參數2是LRESULT指針,用于返回結果,但函數不返回值。</P>
<P
align=justify>對應地,還有把一定范圍的控制子窗口的某個通知消息映射到一個消息處理函數的映射宏,這類宏包括ON__CONTROL_RANGE和ON_NOTIFY_RANGE。這類宏帶有參數,需要指定控制子窗口ID的范圍和通知消息,以及消息處理函數。</P>
<P
align=justify>對于ON__CONTROL_RANGE,是將消息映射條目的第一個成員的nMessage指定為WM_COMMAND,但是第二個成員nCode是特定的通知碼,第三個成員nID和第四個成員nLastID等于指定了控制窗口ID的范圍。消息處理函數的原型是void
(UINT),參數表示要處理的通知消息是哪個ID的控制子窗口發送的,函數不返回值。</P>
<P
align=justify>對于ON__NOTIFY_RANGE,消息映射條目的第一個成員的nMessage被指定為WM_NOTIFY,第二個成員nCode是特定的通知碼,第三個成員nID和第四個成員nLastID指定了控制窗口ID的范圍。消息處理函數的原型是void
(UINT, NMHDR*,
LRESULT*),參數1表示要處理的通知消息是哪個ID的控制子窗口發送的,參數2是NMHDR指針,參數3是LRESULT指針,用于返回結果,但函數不返回值。</P>
<P align=justify>(4)用于用戶界面接口狀態更新的ON_UPDATE_COMMAND_UI宏</P>
<P
align=justify>這類宏被映射到消息WM_COMMND上,帶有兩個參數,需要指定用戶接口對象ID和消息處理函數。消息映射條目的第一個成員nMessage被指定為WM_COMMAND,第二個成員nCode被指定為-1,第三個成員nID和第四個成員nLastID都指定為用戶接口對象ID。消息處理函數的原型是
void (CCmdUI*),參數指向一個CCmdUI對象,不返回值。</P>
<P
align=justify>對應地,有更新一定ID范圍的用戶接口對象的宏ON_UPDATE_COMMAND_UI_RANGE,此宏帶有三個參數,用于指定用戶接口對象ID的范圍和消息處理函數。消息映射條目的第一個成員nMessage被指定為WM_COMMAND,第二個成員nCode被指定為-1,第三個成員nID和第四個成員nLastID用于指定用戶接口對象ID的范圍。消息處理函數的原型是
void (CCmdUI*),參數指向一個CCmdUI對象,函數不返回值。之所以不用當前用戶接口對象ID作為參數,是因為CCmdUI對象包含了有關信息。</P>
<P align=justify>(5)用于其他消息的宏</P>
<P
align=justify>例如用于用戶定義消息的ON_MESSAGE。這類宏帶有參數,需要指定消息ID和消息處理函數。消息映射條目的第一個成員nMessage被指定為消息ID,第二個成員nCode被指定為0,第三個成員nID和第四個成員也是0。消息處理的原型是LRESULT
(WPARAM, LPARAM),參數1和參數2是消息參數wParam和lParam,返回LRESULT類型的值。</P>
<P align=justify>(6)擴展消息映射宏</P>
<P
align=justify>很多普通消息映射宏都有對應的擴展消息映射宏,例如:ON_COMMAND對應的ON_COMMAND_EX,ON_ONTIFY對應的ON_ONTIFY_EX,等等。擴展宏除了具有普通宏的功能,還有特別的用途。關于擴展宏的具體討論和分析,見4.4.3.2節。</P>
<P align=justify>作為一個總結,下表列出了這些常用的消息映射宏。</P>
<P align=center>表4-1 常用的消息映射宏</P>
<P align=left>
<TABLE cellSpacing=1 cellPadding=7 width=499 border=1>
<TBODY>
<TR>
<TD vAlign=top width="41%">
<P align=justify>消息映射宏 </P></TD>
<TD vAlign=top width="59%">
<P align=justify>用途 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_COMMAND </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把command message映射到相應的函數 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_CONTROL </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把control notification
message映射到相應的函數。MFC根據不同的控制消息,在此基礎上定義了更具體的宏,這樣用戶在使用時就不需要指定通知代碼ID,如ON_BN_CLICKED。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_MESSAGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把user-defined message.映射到相應的函數 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_REGISTERED_MESSAGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把registered user-defined
message映射到相應的函數,實際上nMessage等于0x0C000,nSig等于宏的消息參數。nSig的真實值為Afxsig_lwl。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_UPDATE_COMMAND_UI </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把user interface user update command message映射到相應的函數上。
</P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_COMMAND_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范圍內的command IDs 映射到相應的函數上 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_UPDATE_COMMAND_UI_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范圍內的user interface user update command
message映射到相應的函數上 </P></TD></TR>
<TR>
<TD vAlign=top width="41%">
<P align=justify>ON_CONTROL_RANGE </P></TD>
<TD vAlign=top width="59%">
<P align=justify>把一定范圍內的control notification message映射到相應的函數上
</P></TD></TR></TBODY></TABLE>
<P></P>
<P align=justify></P>
<DIR>
<P align=justify>在表4-1中,宏ON_REGISTERED_MESSAGE的定義如下:</P></DIR>
<P align=justify>#define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn)
\</P>
<P align=justify>{ 0xC000, 0, 0, 0,\</P>
<P align=justify>(UINT)(UINT*)(&nMessageVariable), \</P>
<P align=justify>/*implied 'AfxSig_lwl'*/ \</P>
<P align=justify>(AFX_PMSG)(AFX_PMSGW)(LRESULT\</P>
<P align=justify>(AFX_MSG_CALL CWnd::*)\</P>
<P align=justify>(WPARAM, LPARAM))&memberFxn }</P>
<P
align=justify>從上面的定義可以看出,實際上,該消息被映射到WM_COMMAND(0XC000),指定的registered消息ID存放在nSig域內,nSig的值在這樣的映射條目下隱含地定為AfxSig_lwl。由于ID和正常的nSig域存放的值范圍不同,所以MFC可以判斷出是否是registered消息映射條目。如果是,則使用AfxSig_lwl把消息處理函數轉換成參數1為Word、參數2為long、返回值為long的類型。</P>
<P
align=justify>在介紹完了消息映射的內幕之后,應該討論消息處理過程了。由于CCmdTarge的特殊性和重要性,在4.3節先對其作一個大略的介紹。</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889014></A><A name=_Toc445782417></A><A
name=_Toc452640905></A><A name=_Toc457298973></A><B>CcmdTarget類</B>
<P></P></LI></OL></OL>
<P
align=justify>除了CObject類外,還有一個非常重要的類CCmdTarget。所有響應消息或事件的類都從它派生。例如,CWinapp,CWnd,CDocument,CView,CDocTemplate,CFrameWnd,等等。</P>
<P
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -