Partilhar via


TN006: Mapas de mensagens

Esta nota descreve o recurso de mapa de mensagens MFC.

O problema

O Microsoft Windows implementa funções virtuais em classes de janela que usam seu recurso de mensagens. Devido ao grande número de mensagens envolvidas, fornecer uma função virtual separada para cada mensagem do Windows criaria um vtable proibitivamente grande.

Como o número de mensagens do Windows definidas pelo sistema muda ao longo do tempo e porque os aplicativos podem definir suas próprias mensagens do Windows, os mapas de mensagens fornecem um nível de indireção que impede que as alterações na interface quebrem o código existente.

Visão geral

MFC fornece uma alternativa para a instrução switch que foi usado em programas tradicionais baseados no Windows para lidar com mensagens enviadas para uma janela. Um mapeamento de mensagens para métodos pode ser definido para que, quando uma mensagem é recebida por uma janela, o método apropriado seja chamado automaticamente. Este recurso de mapa de mensagens foi projetado para se assemelhar a funções virtuais, mas tem benefícios adicionais que não são possíveis com funções virtuais C++.

Definindo um mapa de mensagens

A macro DECLARE_MESSAGE_MAP declara três membros para uma classe.

  • Uma matriz privada de entradas AFX_MSGMAP_ENTRY chamada _messageEntries.

  • Uma estrutura de AFX_MSGMAP protegida chamada messageMap que aponta para a matriz _messageEntries .

  • Uma função virtual protegida chamada GetMessageMap que retorna o endereço de messageMap.

Esta macro deve ser colocada na declaração de qualquer classe usando mapas de mensagem. Por convenção, está no final da declaração de classe. Por exemplo:

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

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

    DECLARE_MESSAGE_MAP()
};

Este é o formato gerado por AppWizard e ClassWizard quando eles criam novas classes. Os colchetes //{{ e //}} são necessários para o ClassWizard.

A tabela do mapa de mensagens é definida usando um conjunto de macros que se expandem para entradas de mapa de mensagens. Uma tabela começa com uma chamada de macro BEGIN_MESSAGE_MAP , que define a classe que é manipulada por esse mapa de mensagens e a classe pai para a qual as mensagens não tratadas são passadas. A tabela termina com a chamada da macro END_MESSAGE_MAP.

Entre essas duas chamadas de macro há uma entrada para cada mensagem a ser tratada por este mapa de mensagens. Cada mensagem padrão do Windows tem uma macro do formulário ON_WM_MESSAGE_NAME que gera uma entrada para essa mensagem.

Uma assinatura de função padrão foi definida para descompactar os parâmetros de cada mensagem do Windows e fornecer segurança de tipo. Estas assinaturas podem ser encontradas no arquivo Afxwin.h na declaração da CWnd. Cada um é marcado com a palavra-chave afx_msg para facilitar a identificação.

Observação

ClassWizard requer que você use a palavra-chave afx_msg em suas declarações do manipulador de mapa de mensagens.

Estas assinaturas de função foram derivadas usando uma convenção simples. O nome da função começa sempre com "On". Segue-se o nome da mensagem do Windows com o "WM_" removido e a primeira letra de cada palavra em maiúsculas. A ordenação dos parâmetros é wParam seguida por LOWORD(lParam) e depois HIWORD(lParam). Os parâmetros não utilizados não são passados. Todas as alças encapsuladas por classes MFC são convertidas em ponteiros para os objetos MFC apropriados. O exemplo a seguir mostra como manipular a mensagem WM_PAINT e fazer com que a CMyWnd::OnPaint função seja chamada:

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

A tabela de mapa de mensagens deve ser definida fora do escopo de qualquer definição de função ou classe. Não deve ser colocado num bloco "C" externo.

Observação

O ClassWizard modificará as entradas do mapa de mensagens que ocorrem entre o colchete de comentários //{{ e //}}.

Mensagens do Windows definidas pelo usuário

Mensagens definidas pelo usuário podem ser incluídas em um mapa de mensagens usando a macro ON_MESSAGE . Esta macro aceita um número de mensagem e um método do formulário:

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

Neste exemplo, estabelecemos um manipulador para uma mensagem personalizada que tem um ID de mensagem do Windows derivado da base de WM_USER padrão para mensagens definidas pelo usuário. O exemplo a seguir mostra como chamar esta função:

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

O intervalo de mensagens definidas pelo usuário que usam essa abordagem deve estar no intervalo WM_USER a 0x7fff.

Observação

ClassWizard não suporta a inserção de rotinas de manipulador de ON_MESSAGE através da interface do utilizador do ClassWizard. Você deve inseri-los manualmente a partir do editor do Visual C++. O ClassWizard analisará essas entradas e permitirá que você as navegue como qualquer outra entrada de mapa de mensagens.

Mensagens registadas do Windows

A função RegisterWindowMessage é usada para definir uma nova mensagem de janela que é garantidamente única em todo o sistema. A macro ON_REGISTERED_MESSAGE é usada para manipular essas mensagens. Esta macro aceita um nome de uma variável UINT NEAR que contém o ID de mensagem do Windows registrado. Por exemplo

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

A variável de ID de mensagem registrada do Windows (WM_FIND neste exemplo) deve ser uma variável NEAR devido à maneira como ON_REGISTERED_MESSAGE é implementada.

O intervalo de mensagens definidas pelo usuário que usam essa abordagem estará no intervalo 0xC000 a 0xFFFF.

Observação

ClassWizard não suporta a inserção de rotinas de manipulador de ON_REGISTERED_MESSAGE a partir da interface do usuário ClassWizard. Você deve inseri-los manualmente a partir do editor de texto. O ClassWizard analisará essas entradas e permitirá que você as navegue como qualquer outra entrada de mapa de mensagens.

Mensagens de comando

As mensagens de comando de menus e aceleradores são tratadas em mapas de mensagens com a macro ON_COMMAND. Esta macro aceita um ID de comando e um método. Somente a mensagem WM_COMMAND específica que tem um wParam igual ao ID de comando especificado é manipulada pelo método especificado na entrada message-map. As funções de membro do manipulador de comandos não usam parâmetros e retornam void. A macro tem a seguinte forma:

ON_COMMAND(id, memberFxn)

As mensagens de atualização de comando são encaminhadas pelo mesmo mecanismo, mas usam a macro ON_UPDATE_COMMAND_UI. As funções membro do gestor de atualização de comando aceitam um único parâmetro, um ponteiro para um objeto CCmdUI, e retornam void. A macro tem uma forma

ON_UPDATE_COMMAND_UI(id, memberFxn)

Os usuários avançados podem usar a macro ON_COMMAND_EX, que é uma forma estendida de manipuladores de mensagens de comando. A macro fornece um superconjunto da funcionalidade ON_COMMAND. As funções de membro do manipulador de comandos estendido usam um único parâmetro, um UINT que contém a ID do comando, e retornam um BOOL. O valor de retorno deve ser TRUE para indicar que o comando foi manipulado. Caso contrário, o roteamento continuará para outros objetos de destino de comando.

Exemplos destes formulários:

  • Dentro do Resource.h (geralmente gerado pelo Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Dentro da declaração de classe

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Dentro da definição do mapa de mensagens

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • No ficheiro de implementação

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

Os usuários avançados podem lidar com uma variedade de comandos usando um único manipulador de comando: ON_COMMAND_RANGE ou ON_COMMAND_RANGE_EX. Consulte a documentação do produto para obter mais informações sobre essas macros.

Observação

O ClassWizard oferece suporte à criação de manipuladores ON_COMMAND e ON_UPDATE_COMMAND_UI, mas não oferece suporte à criação de manipuladores ON_COMMAND_EX ou ON_COMMAND_RANGE. No entanto, o Assistente de Classe analisará e permitirá que você navegue por todas as quatro variantes do manipulador de comandos.

Controlar mensagens de notificação

As mensagens que são enviadas de controles filho para uma janela têm um bit extra de informação em sua entrada de mapa de mensagem: o ID do controle. O manipulador de mensagens especificado em uma entrada de mapa de mensagens é chamado somente se as seguintes condições forem verdadeiras:

  • O código de notificação de controle (palavra alta de lParam), como BN_CLICKED, corresponde ao código de notificação especificado na entrada do mapa de mensagens.

  • O ID de controle (wParam) corresponde ao ID de controle especificado na entrada de mapa de mensagem.

Mensagens de notificação de controlos personalizados podem usar a macro ON_CONTROL para definir uma entrada no mapa de mensagens com um código de notificação personalizado. Esta macro tem a forma

ON_CONTROL(wNotificationCode, id, memberFxn)

Para uso avançado ON_CONTROL_RANGE pode ser utilizado para lidar com uma notificação de controlo específica de um intervalo de controlos usando o mesmo gestor.

Observação

ClassWizard não suporta a criação de um manipulador de ON_CONTROL ou ON_CONTROL_RANGE na interface do usuário. Você deve inseri-los manualmente com o editor de texto. O ClassWizard analisará essas entradas e permitirá que você as navegue como qualquer outra entrada de mapa de mensagens.

Os Controles Comuns do Windows usam o WM_NOTIFY mais poderoso para notificações de controle complexas. Esta versão do MFC tem suporte direto para essa nova mensagem usando as macros ON_NOTIFY e ON_NOTIFY_RANGE. Consulte a documentação do produto para obter mais informações sobre essas macros.

Ver também

Notas técnicas por número
Notas técnicas por categoria