共用方式為


TN006:訊息映射

此附註描述 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 巨集,直接支援此新訊息。 如需這些巨集的詳細資訊,請參閱產品文件。

另請參閱

依編號的技術注意事項
依類別排序的技術注意事項