分享方式:


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 等於指定命令識別碼的特定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 宏,直接支援此新訊息。 如需這些宏的詳細資訊,請參閱產品檔。

另請參閱

依編號顯示的技術提示
依分類區分的技術提示