Compartilhar via


TN006: mapas de mensagem

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 uma vtable proibitivamente grande.

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

Visão geral

O MFC fornece uma alternativa à instrução switch usada 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 for recebida por uma janela, o método apropriado seja chamado automaticamente. Essa instalação de mapa de mensagens foi projetada para se assemelhar a funções virtuais, mas tem benefícios adicionais que não são possíveis com funções virtuais C++.

Como definir 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.

Essa macro deve ser colocada na declaração de qualquer classe usando mapas de mensagens. Por convenção, ela 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()
};

Esse é o formato gerado por AppWizard e ClassWizard quando eles criam classes. Os colchetes //{{ e //}} são necessários para 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 manipulada por esse mapa de mensagens e a classe pai para a qual as mensagens sem tratamento são passadas. A tabela termina com a chamada de 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 na forma ON_WM_MESSAGE_NAME que gera uma entrada para essa mensagem.

Uma assinatura de função padrão foi definida para desempacotar os parâmetros de cada mensagem do Windows e fornecer segurança de tipos. Essas assinaturas podem ser encontradas no arquivo Afxwin.h na declaração de 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 nas declarações do manipulador de mapa de mensagens.

Essas assinaturas de função foram derivadas usando uma convenção simples. O nome da função sempre começa com "On". Isso é seguido pelo nome da mensagem do Windows com o "WM_" removido e a primeira letra de cada palavra maiúscula. A ordenação dos parâmetros é wParam seguido por LOWORD(lParam) e depois HIWORD(lParam). Parâmetros não utilizados não são passados. Todos os identificadores encapsulados por classes MFC são convertidos em ponteiros para os objetos MFC apropriados. O seguinte exemplo mostra como lidar com a mensagem WM_PAINT e fazer com que a função CMyWnd::OnPaint 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. Ela não deve ser colocada em um bloco "C" extern.

Observação

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

Mensagens do Windows definidas pelo usuário

As mensagens definidas pelo usuário podem ser incluídas em um mapa de mensagens usando a macro ON_MESSAGE. Essa macro aceita um número de mensagem e um método na forma:

    // 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 uma ID de mensagem do Windows derivada da base de WM_USER padrão para mensagens definidas pelo usuário. O seguinte exemplo mostra como chamar esse manipulador:

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

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

Observação

O ClassWizard não dá suporte à inserção ON_MESSAGE rotinas de manipulador da interface do usuário ClassWizard. Você deve inseri-las manualmente no editor do Visual C++. ClassWizard analisará essas entradas e permitirá que você navegue por elas como qualquer outra entrada de mapa de mensagens.

Mensagens registradas do Windows

A função RegisterWindowMessage é usada para definir uma nova mensagem de janela que com certeza seja exclusiva em todo o sistema. A macro ON_REGISTERED_MESSAGE é usada para lidar com essas mensagens. Essa macro aceita um nome de uma variável UINT NEAR que contém a ID da mensagem do Windows registrada. 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 do Windows registrada (WM_FIND neste exemplo) deve ser uma variável NEAR devido à maneira como o ON_REGISTERED_MESSAGE é implementado.

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

Observação

O ClassWizard não dá suporte à inserção de rotinas de manipulador ON_REGISTERED_MESSAGE da interface do usuário ClassWizard. Você deve inseri-las manualmente no editor de texto. ClassWizard analisará essas entradas e permitirá que você navegue por elas como qualquer outra entrada de mapa de mensagens.

Mensagens de comando

Mensagens de comando de menus e aceleradores são manipuladas em mapas de mensagens com a macro ON_COMMAND. Essa macro aceita uma ID de comando e um método. Somente a mensagem de WM_COMMAND específica que tem um wParam igual à ID de comando especificada é tratada pelo método especificado na entrada do mapa de mensagens. As funções de membro do manipulador de comando não têm parâmetros e retornam void. A macro tem a seguinte forma:

ON_COMMAND(id, memberFxn)

As mensagens de atualização de comando são roteadas pelo mesmo mecanismo, mas usam a macro ON_UPDATE_COMMAND_UI. As funções de membro do manipulador de atualização de comando usam um só parâmetro, um ponteiro para um objeto CCmdUI, e retornam void. A macro tem o formulário

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 comando estendido usam um só parâmetro, um UINT que contém a ID de comando, e retornam um BOOL. O valor retornado deve ser TRUE para indicar que o comando foi tratado. Caso contrário, o roteamento continuará para outros objetos de destino de comando.

Exemplos dessas forma:

  • 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 arquivo 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 um intervalo de comandos usando um só manipulador de comandos: ON_COMMAND_RANGE ou ON_COMMAND_RANGE_EX. Confira a documentação do produto para obter mais informações sobre essas macros.

Observação

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

Mensagens de notificação de controle

As mensagens enviadas de controles filho para uma janela têm um bit extra de informações na entrada do mapa de mensagens: a ID do controle. O manipulador de mensagens especificado em uma entrada de mapa de mensagem será 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.

  • A ID do controle (wParam) corresponde à ID de controle especificada na entrada do mapa de mensagens.

Mensagens de notificação de controle personalizado podem usar a macro ON_CONTROL para definir uma entrada de mapa de mensagem com um código de notificação personalizado. Essa macro tem a forma

ON_CONTROL(wNotificationCode, id, memberFxn)

Para uso avançado ON_CONTROL_RANGE pode ser usado para lidar com uma notificação de controle específica de um intervalo de controles com o mesmo manipulador.

Observação

O ClassWizard não dá suporte à 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. ClassWizard analisará essas entradas e permitirá que você navegue por elas como qualquer outra entrada de mapa de mensagens.

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

Confira também

Observações técnicas por número
Observações técnicas por categoria