Condividi tramite


TN006: mappe messaggi

Questa nota descrive la struttura mappa messaggi MFC.

Problema

Microsoft Windows implementa funzioni virtuali nelle classi finestra che usano la relativa funzionalità di messaggistica. A causa del numero elevato di messaggi coinvolti, la fornitura di una funzione virtuale separata per ogni messaggio di Windows creerebbe una vtable di dimensioni proibitive.

Poiché il numero di messaggi windows definiti dal sistema cambia nel tempo e poiché le applicazioni possono definire i propri messaggi di Windows, le mappe messaggi forniscono un livello di riferimento indiretto che impedisce alle modifiche all'interfaccia di interrompere il codice esistente.

Panoramica

MFC offre un'alternativa all'istruzione switch usata nei programmi tradizionali basati su Windows per gestire i messaggi inviati a una finestra. È possibile definire un mapping dai messaggi ai metodi in modo che, quando un messaggio viene ricevuto da una finestra, il metodo appropriato viene chiamato automaticamente. Questa struttura message-map è progettata per assomigliare a funzioni virtuali, ma offre vantaggi aggiuntivi non possibili con le funzioni virtuali C++.

Definizione di una mappa messaggi

La macro DECLARE_MESSAGE_MAP dichiara tre membri per una classe.

  • Matrice privata di voci di AFX_MSGMAP_ENTRY denominate _messageEntries.

  • Struttura protetta AFX_MSGMAP denominata messageMap che punta alla matrice di _messageEntries .

  • Funzione virtuale protetta denominata GetMessageMap che restituisce l'indirizzo di messageMap.

Questa macro deve essere inserita nella dichiarazione di qualsiasi classe utilizzando mappe messaggi. Per convenzione, si trova alla fine della dichiarazione di classe. Ad esempio:

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

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

    DECLARE_MESSAGE_MAP()
};

Questo è il formato generato da AppWizard e ClassWizard quando creano nuove classi. Le parentesi quadre //{{ e //}} sono necessarie per ClassWizard.

La tabella della mappa messaggi viene definita usando un set di macro che si espandono alle voci della mappa messaggi. Una tabella inizia con una chiamata di macro BEGIN_MESSAGE_MAP , che definisce la classe gestita da questa mappa messaggi e la classe padre a cui vengono passati messaggi non gestiti. La tabella termina con la chiamata di macro END_MESSAGE_MAP .

Tra queste due chiamate di macro è una voce per ogni messaggio che deve essere gestito da questa mappa messaggi. Ogni messaggio di Windows standard include una macro del modulo ON_WM_MESSAGE_NAME che genera una voce per tale messaggio.

È stata definita una firma di funzione standard per decomprimere i parametri di ogni messaggio di Windows e garantire la sicurezza dei tipi. Queste firme possono essere trovate nel file Afxwin.h nella dichiarazione di CWnd. Ognuno di essi viene contrassegnato con la parola chiave afx_msg per facilitare l'identificazione.

Nota

ClassWizard richiede l'uso della parola chiave afx_msg nelle dichiarazioni del gestore della mappa messaggi.

Queste firme di funzione sono state derivate usando una convenzione semplice. Il nome della funzione inizia sempre con "On". Questo è seguito dal nome del messaggio di Windows con il "WM_" rimosso e la prima lettera di ogni parola maiuscola. L'ordinamento dei parametri è wParam seguito da LOWORD(lParam) e quindi HIWORD(lParam). I parametri inutilizzati non vengono passati. Tutti gli handle di cui viene eseguito il wrapping da classi MFC vengono convertiti in puntatori agli oggetti MFC appropriati. Nell'esempio seguente viene illustrato come gestire il messaggio WM_PAINT e fare in modo che la CMyWnd::OnPaint funzione venga chiamata:

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

La tabella della mappa dei messaggi deve essere definita all'esterno dell'ambito di qualsiasi definizione di funzione o classe. Non deve essere inserito in un blocco "C" extern.

Nota

ClassWizard modificherà le voci della mappa messaggi che si verificano tra la parentesi di commento //{{ e //}} .

Messaggi di Windows definiti dall'utente

I messaggi definiti dall'utente possono essere inclusi in una mappa messaggi usando la macro ON_MESSAGE . Questa macro accetta un numero di messaggio e un metodo del modulo:

    // 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 questo esempio viene stabilito un gestore per un messaggio personalizzato con un ID messaggio di Windows derivato dalla base standard WM_Uedizione Standard R per i messaggi definiti dall'utente. L'esempio seguente illustra come chiamare questo gestore:

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

L'intervallo di messaggi definiti dall'utente che usano questo approccio deve essere compreso nell'intervallo WM_Uedizione Standard R da 0x7fff.

Nota

ClassWizard non supporta l'immissione di routine del gestore ON_MESSAGE dall'interfaccia utente ClassWizard. È necessario immetterli manualmente dall'editor di Visual C++. ClassWizard analizzerà queste voci e le consente di sfogliarle esattamente come qualsiasi altra voce della mappa dei messaggi.

Messaggi di Windows registrati

La funzione RegisterWindowMessage viene usata per definire un nuovo messaggio di finestra che è garantito che sia univoco in tutto il sistema. La macro ON_REGISTERED_MESSAGE viene utilizzata per gestire questi messaggi. Questa macro accetta un nome di una variabile NEAR UINT contenente l'ID messaggio di Windows registrato. Ad esempio:

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

La variabile ID messaggio di Windows registrata (WM_FIND in questo esempio) deve essere una variabile NEAR a causa della modalità di implementazione ON_REGISTERED_MESSAGE.

L'intervallo di messaggi definiti dall'utente che usano questo approccio sarà compreso nell'intervallo 0xC000 da 0xFFFF.

Nota

ClassWizard non supporta l'immissione di routine del gestore ON_REGISTERED_MESSAGE dall'interfaccia utente ClassWizard. È necessario immetterli manualmente dall'editor di testo. ClassWizard analizzerà queste voci e le consente di sfogliarle esattamente come qualsiasi altra voce della mappa dei messaggi.

Messaggi di comando

I messaggi di comando dai menu e dagli acceleratori vengono gestiti nelle mappe messaggi con la macro ON_COMMAND. Questa macro accetta un ID comando e un metodo. Solo il messaggio di WM_COMMAND specifico con wParam uguale all'ID comando specificato viene gestito dal metodo specificato nella voce message-map. Le funzioni membro del gestore comandi non accettano parametri e restituiscono void. La macro ha il formato seguente:

ON_COMMAND(id, memberFxn)

I messaggi di aggiornamento dei comandi vengono instradati tramite lo stesso meccanismo, ma usano invece la macro ON_UPDATE_COMMAND_UI. Le funzioni membro del gestore di aggiornamento dei comandi accettano un singolo parametro, un puntatore a un oggetto CCmdUI e restituiscono void. La macro ha il formato

ON_UPDATE_COMMAND_UI(id, memberFxn)

Gli utenti avanzati possono usare la macro ON_COMMAND_EX, che è una forma estesa di gestori di messaggi di comando. La macro fornisce un superset della funzionalità ON_COMMAND. Le funzioni membro del gestore comandi estese accettano un singolo parametro, un UINT che contiene l'ID comando e restituiscono un valore BOOL. Il valore restituito deve essere TRUE per indicare che il comando è stato gestito. In caso contrario, il routing continuerà ad altri oggetti di destinazione del comando.

Esempi di queste forme:

  • All'interno di Resource.h (in genere generato da Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • All'interno della dichiarazione di classe

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • All'interno della definizione della mappa messaggi

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • Nel file di implementazione

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

Gli utenti avanzati possono gestire un intervallo di comandi usando un singolo gestore comandi: ON_COMMAND_RANGE o ON_COMMAND_RANGE_EX. Per altre informazioni su queste macro, vedere la documentazione del prodotto.

Nota

ClassWizard supporta la creazione di gestori di ON_COMMAND e ON_UPDATE_COMMAND_UI, ma non supporta la creazione di gestori ON_COMMAND_EX o ON_COMMAND_RANGE. Tuttavia, la Creazione guidata classe analizzerà e consentirà di esplorare tutte e quattro le varianti del gestore di comandi.

Controllare i messaggi di notifica

I messaggi inviati dai controlli figlio a una finestra hanno un bit aggiuntivo di informazioni nella voce della mappa messaggi: l'ID del controllo. Il gestore di messaggi specificato in una voce della mappa messaggi viene chiamato solo se le condizioni seguenti sono vere:

  • Il codice di notifica del controllo (parola alta di lParam), ad esempio BN_CLICKED, corrisponde al codice di notifica specificato nella voce message-map.

  • L'ID di controllo (wParam) corrisponde all'ID di controllo specificato nella voce message-map.

I messaggi di notifica di controllo personalizzati possono utilizzare la macro ON_CONTROL per definire una voce della mappa messaggi con un codice di notifica personalizzato. Questa macro ha il formato

ON_CONTROL(wNotificationCode, id, memberFxn)

Per l'utilizzo avanzato ON_CONTROL_RANGE può essere usato per gestire una notifica di controllo specifica da un intervallo di controlli con lo stesso gestore.

Nota

ClassWizard non supporta la creazione di un gestore di ON_CONTROL o ON_CONTROL_RANGE nell'interfaccia utente. È necessario immetterli manualmente con l'editor di testo. ClassWizard analizzerà queste voci e le consente di sfogliarle esattamente come qualsiasi altra voce della mappa dei messaggi.

I controlli comuni di Windows usano le WM_NOTIFY più potenti per le notifiche di controllo complesse. Questa versione di MFC supporta direttamente questo nuovo messaggio usando le macro ON_NOTIFY e ON_NOTIFY_RANGE. Per altre informazioni su queste macro, vedere la documentazione del prodotto.

Vedi anche

Note tecniche per numero
Note tecniche per categoria