此附註描述 MFC 訊息對應設施。
問題
Microsoft Windows 會在使用其傳訊設施的視窗類別中實作虛擬函式。 由於涉及大量訊息,為每個 Windows 訊息提供個別的虛擬函式,將會建立一個過於龐大的 vtable。
由於系統定義的 Windows 訊息數目會隨著時間而變更,而且因為應用程式可以定義自己的 Windows 訊息,因此訊息對應會提供一種間接存取層級,以防止介面變更中斷現有的程式代碼。
概觀
MFC 提供 switch 語句的替代方案,該語句用於傳統 Windows 程式來處理傳送至視窗的訊息。 您可以定義從訊息到方法的對應,以便在視窗接收訊息時自動呼叫適當的方法。 此訊息映射設施的設計類似於虛擬函式,但具備 C++ 虛擬函式無法提供的額外優點。
定義訊息地圖
DECLARE_MESSAGE_MAP 巨集會宣告一個類別的三個成員。
名為 _messageEntries 的 AFX_MSGMAP_ENTRY 條目的私有陣列。
稱為 messageMap 的受保護AFX_MSGMAP結構,指向 _messageEntries 陣列。
名為
GetMessageMap
的受保護虛擬函式,會傳回 messageMap 的位址。
這個巨集應該放在任何使用訊息映射的類別定義中。 按照慣例,通常在類別宣告的結尾會放置它。 例如:
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
這是 AppWizard 和 ClassWizard 在建立新類別時所產生的格式。 ClassWizard 需要 //{{ 和 //}} 括弧。
訊息映射表是透過使用一組展開為訊息映射條目的巨集來定義的。 數據表會從 BEGIN_MESSAGE_MAP 巨集呼叫開始,這個呼叫會定義這個訊息對應所處理的類別,以及傳遞未處理訊息的父類別。 數據表會以巨集呼叫 END_MESSAGE_MAP 結尾。
在這兩個巨集呼叫之間,是此訊息對應所處理的每個訊息條目。 每個標準 Windows 訊息都有一種格式的巨集ON_WM_MESSAGE_NAME,用於產生該訊息的項目。
已定義標準函數簽名,用於拆解每個 Windows 訊息的參數並提供類型安全性。 這些簽章可以在 CWnd 宣告的 Afxwin.h 檔案中找到。 每個標記都有關鍵詞 afx_msg ,以便輕鬆識別。
備註
ClassWizard 要求您在訊息對應處理程式宣告中使用 afx_msg 關鍵詞。
這些函式簽章是使用簡單的慣例來衍生的。 函式的名稱一律以 "On
「開頭。 接著是移除「WM_」的 Windows 訊息名稱,其中每個單字的第一個字母都要大寫。 參數的順序是 wParam ,後面接著 LOWORD
[lParam] 和 HIWORD
[lParam]。 未使用的參數不會傳遞。 MFC 類別所包裝的任何句柄都會轉換成適當 MFC 物件的指標。 下列範例示範如何處理WM_PAINT訊息,以致呼叫CMyWnd::OnPaint
函數:
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
訊息對應表必須在任何函式或類別定義的範圍之外定義。 它不應該放在 extern “C” 區塊中。
備註
ClassWizard 會修改發生在 //{{ 和 //}} 批注括弧之間的訊息映射條目。
使用者定義 Windows 訊息
使用者定義的訊息可以通過使用 ON_MESSAGE 巨集來包含在訊息對應中。 此巨集接受訊息編號和格式的方法:
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
在此範例中,我們會為自定義訊息建立處理程式,此訊息的處理程式具有衍生自使用者定義訊息之標準WM_USER基底的 Windows 訊息標識碼。 下列範例示範如何呼叫此處理程式:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
使用此方法的使用者定義訊息範圍必須介於 WM_USER 至 0x7fff 之間。
備註
ClassWizard 不支援從 ClassWizard 使用者介面輸入ON_MESSAGE處理程式例程。 您必須從 Visual C++ 編輯器手動輸入它們。 ClassWizard 會剖析這些專案,並讓您像任何其他訊息對應項目一樣瀏覽這些專案。
已註冊的 Windows 訊息
RegisterWindowMessage 函式可用來定義保證在整個系統中是唯一的新視窗訊息。 巨集ON_REGISTERED_MESSAGE用來處理這些訊息。 此巨集接受 UINT NEAR 變數的名稱,其中包含已註冊的 Windows 訊息識別碼。 例如:
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
已註冊的 Windows 訊息標識碼變數 (在此範例中WM_FIND) 必須是 NEAR 變數,因為實作ON_REGISTERED_MESSAGE的方式。
使用此方法的使用者定義訊息範圍將會在0xC000 0xFFFF範圍內。
備註
ClassWizard 不支援從 ClassWizard 使用者介面輸入ON_REGISTERED_MESSAGE處理程式例程。 您必須從文字編輯器手動輸入它們。 ClassWizard 會剖析這些專案,並讓您像任何其他訊息對應項目一樣瀏覽這些專案。
命令訊息
從功能表和加速鍵發出的命令訊息會在訊息對應中使用 ON_COMMAND 巨集進行處理。 這個巨集接受命令標識碼和方法。 只有 wParam 等於具體命令 ID 的特定 WM_COMMAND 訊息,由消息映射條目中指定的方法處理。 命令處理程式成員函式不需要參數並傳回 void
。 巨集的格式如下:
ON_COMMAND(id, memberFxn)
命令更新訊息會透過相同的機制路由傳送,但改用 ON_UPDATE_COMMAND_UI 巨集。 命令更新處理常式的成員函式接受一個參數,即指向CCmdUI物件的指標,並返回void
。 巨集的格式為
ON_UPDATE_COMMAND_UI(id, memberFxn)
進階使用者可以使用ON_COMMAND_EX巨集,這是命令訊息處理程式的擴充形式。 巨集提供比ON_COMMAND功能更豐富的超集功能。 擴充的命令處理程式成員函式會採用單一參數、包含命令標識碼的 UINT ,並傳回 BOOL。 傳回值應該是 TRUE ,表示已處理命令。 否則,路由會繼續傳送至其他命令目標物件。
這些表單的範例:
在 Resource.h 內 (通常是由 Visual C++ 產生)
#define ID_MYCMD 100 #define ID_COMPLEX 101
在類別定義內
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
在訊息映射定義中
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
在實作檔案中
void CMyClass::OnMyCommand() { // handle the command } void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI) { // set the UI state with pCmdUI } BOOL CMyClass::OnComplexCommand(UINT nID) { // handle the command return TRUE; }
進階使用者可以使用單一命令處理程式來處理一系列命令: ON_COMMAND_RANGE 或ON_COMMAND_RANGE_EX。 如需這些巨集的詳細資訊,請參閱產品文件。
備註
ClassWizard 支援建立ON_COMMAND和ON_UPDATE_COMMAND_UI處理程式,但不支援建立ON_COMMAND_EX或ON_COMMAND_RANGE處理程式。 不過,[類別精靈] 會剖析並讓您流覽所有四個命令處理程式變體。
控制通知訊息
從子控制項傳送到視窗的訊息,在其訊息對應專案中包含附加的資訊位元:控制項的識別碼。 只有在下列條件成立時,才會呼叫訊息映射中特定的處理程式:
控制通知代碼( lParam 的高字),例如 BN_CLICKED,符合訊息映射項目中指定的通知碼。
控件識別碼 (wParam) 符合訊息對應專案中指定的控件識別碼。
自定義控件通知訊息可以使用 ON_CONTROL 巨集,以自定義通知代碼定義訊息對應項目。 此巨集具有...格式
ON_CONTROL(wNotificationCode, id, memberFxn)
對於進階應用,ON_CONTROL_RANGE 可用來處理一系列控件中的特定控件通知,這些控件使用相同的處理程式。
備註
ClassWizard 不支援在使用者介面中建立ON_CONTROL或ON_CONTROL_RANGE處理程式。 您必須使用文字編輯器手動輸入它們。 ClassWizard 會剖析這些專案,並讓您像任何其他訊息對應項目一樣瀏覽這些專案。
Windows 通用控件會針對複雜的控制項通知使用更強大的 WM_NOTIFY 。 此版本的 MFC 使用 ON_NOTIFY 和 ON_NOTIFY_RANGE 巨集,直接支援此新訊息。 如需這些巨集的詳細資訊,請參閱產品文件。