TN006: Meldungszuordnungen

In diesem Hinweis wird die MFC-Nachrichtenzuordnungseinrichtung beschrieben.

Problemstellung

Microsoft Windows implementiert virtuelle Funktionen in Fensterklassen, die ihre Messaging-Funktion verwenden. Aufgrund der großen Anzahl der beteiligten Nachrichten würde die Bereitstellung einer separaten virtuellen Funktion für jede Windows-Nachricht eine unerschwingliche vtable erstellen.

Da sich die Anzahl systemdefinierter Windows-Nachrichten im Laufe der Zeit ändert und Anwendungen ihre eigenen Windows-Nachrichten definieren können, stellen Nachrichtenzuordnungen eine Dereferenzierungsebene bereit, die verhindert, dass Schnittstellenänderungen vorhandenen Code unterbrechen.

Überblick

MFC bietet eine Alternative zu der Switch-Anweisung, die in herkömmlichen Windows-basierten Programmen verwendet wurde, um Nachrichten zu verarbeiten, die an ein Fenster gesendet werden. Eine Zuordnung von Nachrichten zu Methoden kann definiert werden, sodass die entsprechende Methode automatisch aufgerufen wird, wenn eine Nachricht von einem Fenster empfangen wird. Diese Nachrichtenzuordnungseinrichtung ist so konzipiert, dass sie virtuellen Funktionen ähnelt, aber mit virtuellen C++-Funktionen keine zusätzlichen Vorteile bietet.

Definieren einer Nachrichtenzuordnung

Das DECLARE_MESSAGE_MAP-Makro deklariert drei Member für eine Klasse.

  • Ein privates Array von AFX_MSGMAP_ENTRY Einträgen, die als _messageEntries bezeichnet werden.

  • Eine geschützte AFX_MSGMAP Struktur namens messageMap , die auf das _messageEntries Array verweist.

  • Eine geschützte virtuelle Funktion, die die GetMessageMap Adresse von messageMap zurückgibt.

Dieses Makro sollte in die Deklaration einer beliebigen Klasse mit Nachrichtenzuordnungen eingefügt werden. In der Konvention befindet es sich am Ende der Klassendeklaration. Beispiel:

class CMyWnd : public CMyParentWndClass
{
    // my stuff...

protected:
    //{{AFX_MSG(CMyWnd)
    afx_msg void OnPaint();
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

Dies ist das Format, das von AppWizard und ClassWizard generiert wird, wenn sie neue Klassen erstellen. Die Klammern //{{ und //}} sind für ClassWizard erforderlich.

Die Tabelle der Nachrichtenzuordnung wird mithilfe einer Gruppe von Makros definiert, die in Nachrichtenzuordnungseinträge erweitert werden. Eine Tabelle beginnt mit einem BEGIN_MESSAGE_MAP Makroaufruf, der die Klasse definiert, die von dieser Nachrichtenzuordnung behandelt wird, und die übergeordnete Klasse, an die unbehandelte Nachrichten übergeben werden. Die Tabelle endet mit dem END_MESSAGE_MAP Makroaufruf.

Zwischen diesen beiden Makroaufrufen handelt es sich um einen Eintrag für jede Nachricht, die von dieser Nachrichtenzuordnung behandelt werden soll. Jede Windows-Standardnachricht verfügt über ein Makro des Formulars ON_WM_MESSAGE_NAME das einen Eintrag für diese Nachricht generiert.

Eine Standardfunktionssignatur wurde zum Entpacken der Parameter jeder Windows-Nachricht und zur Bereitstellung der Typsicherheit definiert. Diese Signaturen finden Sie in der Datei Afxwin.h in der Deklaration von CWnd. Jeder ist mit dem Schlüsselwort (keyword) afx_msg gekennzeichnet, um eine einfache Identifizierung zu ermöglichen.

Hinweis

"ClassWizard" erfordert, dass Sie die afx_msg Schlüsselwort (keyword) in Den Deklarationen des Nachrichtenzuordnungshandlers verwenden.

Diese Funktionssignaturen wurden mithilfe einer einfachen Konvention abgeleitet. Der Name der Funktion beginnt immer mit "On". Dies folgt dem Namen der Windows-Nachricht, wobei die "WM_" entfernt und der erste Buchstabe jedes Worts großgeschrieben ist. Die Sortierung der Parameter lautet wParam gefolgt von LOWORD(lParam) und dann HIWORD(lParam). Nicht verwendete Parameter werden nicht übergeben. Alle Handles, die von MFC-Klassen umschlossen werden, werden in Zeiger auf die entsprechenden MFC-Objekte konvertiert. Das folgende Beispiel zeigt, wie sie die WM_PAINT Nachricht behandeln und bewirken, dass die CMyWnd::OnPaint Funktion aufgerufen wird:

BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
    //{{AFX_MSG_MAP(CMyWnd)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Die Nachrichtenzuordnungstabelle muss außerhalb des Gültigkeitsbereichs einer beliebigen Funktions- oder Klassendefinition definiert werden. Er sollte nicht in einen externen "C"-Block eingefügt werden.

Hinweis

ClassWizard ändert die Nachrichtenzuordnungseinträge, die zwischen der Kommentarklammer ///{{ und //}} auftreten.

Benutzerdefinierte Windows-Nachrichten

Benutzerdefinierte Nachrichten können mithilfe des ON_MESSAGE-Makros in eine Nachrichtenzuordnung aufgenommen werden. Dieses Makro akzeptiert eine Nachrichtennummer und eine Methode des Formulars:

    // 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()

In diesem Beispiel wird ein Handler für eine benutzerdefinierte Nachricht eingerichtet, die eine Windows-Nachrichten-ID enthält, die von der Standard-WM_USER Basis für benutzerdefinierte Nachrichten abgeleitet ist. Das folgende Beispiel zeigt, wie Sie diesen Handler aufrufen:

CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);

Der Bereich von benutzerdefinierten Nachrichten, die diesen Ansatz verwenden, muss sich im Bereich WM_USER befinden, um 0x7fff.

Hinweis

ClassWizard unterstützt die Eingabe von ON_MESSAGE Handlerroutinen über die ClassWizard-Benutzeroberfläche nicht. Sie müssen sie manuell aus dem Visual C++-Editor eingeben. ClassWizard analysiert diese Einträge und ermöglicht es Ihnen, sie wie alle anderen Nachrichtenzuordnungseinträge zu durchsuchen.

Registrierte Windows-Nachrichten

Die RegisterWindowMessage-Funktion wird verwendet, um eine neue Fenstermeldung zu definieren, die garantiert im gesamten System eindeutig ist. Das Makro ON_REGISTERED_MESSAGE wird verwendet, um diese Nachrichten zu behandeln. Dieses Makro akzeptiert einen Namen einer UINT NEAR-Variable , die die registrierte Windows-Nachrichten-ID enthält. Beispiel:

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()

Die registrierte Windows-Nachrichten-ID-Variable (WM_FIND in diesem Beispiel) muss eine NEAR-Variable sein, da ON_REGISTERED_MESSAGE implementiert wird.

Der Bereich von benutzerdefinierten Nachrichten, die diesen Ansatz verwenden, befindet sich im Bereich 0xC000, um 0xFFFF.

Hinweis

ClassWizard unterstützt die Eingabe von ON_REGISTERED_MESSAGE Handlerroutinen über die ClassWizard-Benutzeroberfläche nicht. Sie müssen sie manuell aus dem Text-Editor eingeben. ClassWizard analysiert diese Einträge und ermöglicht es Ihnen, sie wie alle anderen Nachrichtenzuordnungseinträge zu durchsuchen.

Befehlsmeldungen

Befehlsmeldungen aus Menüs und Zugriffstasten werden in Nachrichtenzuordnungen mit dem ON_COMMAND-Makro behandelt. Dieses Makro akzeptiert eine Befehls-ID und eine Methode. Nur die spezifische WM_COMMAND Nachricht, die eine wParam hat, die der angegebenen Befehls-ID entspricht, wird von der im Nachrichtenzuordnungseintrag angegebenen Methode behandelt. Memberfunktionen des Befehlshandlers verwenden keine Parameter und geben zurück void. Das Makro weist das folgende Formular auf:

ON_COMMAND(id, memberFxn)

Befehlsaktualisierungsmeldungen werden über denselben Mechanismus weitergeleitet, verwenden aber stattdessen das ON_UPDATE_COMMAND_UI Makro. Memberfunktionen des Befehlsaktualisierungshandlers verwenden einen einzelnen Parameter, einen Zeiger auf ein CCmdUI-Objekt und geben zurück void. Das Makro weist das Formular auf

ON_UPDATE_COMMAND_UI(id, memberFxn)

Erweiterte Benutzer können das ON_COMMAND_EX-Makro verwenden, bei dem es sich um eine erweiterte Form von Befehlsnachrichtenhandlern handelt. Das Makro stellt eine Obermenge der ON_COMMAND Funktionalität bereit. Erweiterte Befehlshandler-Memberfunktionen verwenden einen einzelnen Parameter, einen UINT , der die Befehls-ID enthält, und geben einen BOOL zurück. Der Rückgabewert sollte TRUE sein, um anzugeben, dass der Befehl behandelt wurde. Andernfalls wird das Routing weiterhin an andere Befehlszielobjekte weitergeleitet.

Beispiele für diese Formulare:

  • Inside Resource.h (normalerweise von Visual C++generiert)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Innerhalb der Klassendeklaration

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Innerhalb der Definition der Nachrichtenzuordnung

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • In der Implementierungsdatei

    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;
    }
    

Erweiterte Benutzer können einen Bereich von Befehlen mithilfe eines einzelnen Befehlshandlers behandeln: ON_COMMAND_RANGE oder ON_COMMAND_RANGE_EX. Weitere Informationen zu diesen Makros finden Sie in der Produktdokumentation.

Hinweis

ClassWizard unterstützt das Erstellen von ON_COMMAND und ON_UPDATE_COMMAND_UI Handlern, unterstützt jedoch das Erstellen von ON_COMMAND_EX oder ON_COMMAND_RANGE Handlern nicht. Der Klassen-Assistent analysiert jedoch alle vier Befehlshandlervarianten.

Steuern von Benachrichtigungen

Nachrichten, die von untergeordneten Steuerelementen an ein Fenster gesendet werden, enthalten zusätzliche Informationen in ihrem Nachrichtenzuordnungseintrag: die ID des Steuerelements. Der in einem Nachrichtenzuordnungseintrag angegebene Nachrichtenhandler wird nur aufgerufen, wenn die folgenden Bedingungen erfüllt sind:

  • Der Steuerelementbenachrichtigungscode (high word of lParam), z. B. BN_CLICKED, entspricht dem im Nachrichtenzuordnungseintrag angegebenen Benachrichtigungscode.

  • Die Steuerelement-ID (wParam) entspricht der steuerelement-ID, die im Nachrichtenzuordnungseintrag angegeben ist.

Benutzerdefinierte Steuerelementbenachrichtigungen können das ON_CONTROL Makro verwenden, um einen Nachrichtenzuordnungseintrag mit einem benutzerdefinierten Benachrichtigungscode zu definieren. Dieses Makro weist das Formular auf

ON_CONTROL(wNotificationCode, id, memberFxn)

Für die erweiterte Verwendung können ON_CONTROL_RANGE verwendet werden, um eine bestimmte Steuerelementbenachrichtigung aus einem Bereich von Steuerelementen mit demselben Handler zu behandeln.

Hinweis

ClassWizard unterstützt das Erstellen eines ON_CONTROL oder ON_CONTROL_RANGE Handlers auf der Benutzeroberfläche nicht. Sie müssen sie manuell mit dem Text-Editor eingeben. ClassWizard analysiert diese Einträge und ermöglicht es Ihnen, sie wie alle anderen Nachrichtenzuordnungseinträge zu durchsuchen.

Die allgemeinen Windows-Steuerelemente verwenden die leistungsstärkeren WM_NOTIFY für komplexe Steuerelementbenachrichtigungen. Diese Version von MFC unterstützt diese neue Nachricht direkt mithilfe der ON_NOTIFY und ON_NOTIFY_RANGE Makros. Weitere Informationen zu diesen Makros finden Sie in der Produktdokumentation.

Siehe auch

Technische Hinweise – nach Nummern geordnet
Technische Hinweise – nach Kategorien geordnet