Compartir vía


TN006: Mapas de mensajes

En esta nota, se describe la utilidad de mapa de mensajes de MFC.

El problema

Microsoft Windows implementa funciones virtuales en las clases de ventana que usan su utilidad de mensajería. Debido al gran número de mensajes implicados, proporcionar una función virtual independiente para cada mensaje de Windows crearía una tabla virtual prohibitivamente grande.

Dado que el número de mensajes de Windows definidos por el sistema cambia a lo largo del tiempo y dado que las aplicaciones pueden definir sus propios mensajes de Windows, los mapas de mensajes proporcionan un nivel de direccionamiento indirecto que impide que los cambios de interfaz interrumpan el funcionamiento del código existente.

Información general

MFC proporciona una alternativa a la instrucción switch que se usaba en los programas tradicionales basados en Windows para controlar los mensajes enviados a una ventana. Se puede definir una asignación de mensajes a métodos para que cuando una ventana reciba un mensaje, se llame automáticamente al método adecuado. Esta utilidad de mapa de mensajes está diseñada para parecerse a las funciones virtuales, pero tiene ventajas adicionales que no son posibles con las funciones virtuales de C++.

Definición de un mapa de mensajes

La macro DECLARE_MESSAGE_MAP declara tres miembros para una clase.

  • Una matriz privada de entradas AFX_MSGMAP_ENTRY llamada _messageEntries.

  • Una estructura AFX_MSGMAP protegida llamada messageMap que apunta a la matriz _messageEntries.

  • Una función virtual protegida llamada GetMessageMap que devuelve la dirección de messageMap.

Esta macro se debe colocar en la declaración de cualquier clase que use mapas de mensajes. Por convención, se encuentra al final de la declaración de la clase. Por ejemplo:

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

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

    DECLARE_MESSAGE_MAP()
};

Este es el formato generado por AppWizard y ClassWizard cuando crean nuevas clases. Los corchetes //{{ y //}} son necesarios para ClassWizard.

La tabla del mapa de mensajes se define mediante un conjunto de macros que se expanden a entradas de mapa de mensajes. Una tabla comienza con una llamada a la macro BEGIN_MESSAGE_MAP, que define la clase que controla este mapa de mensajes y la clase primaria a la que se pasan los mensajes no controlados. La tabla finaliza con la llamada a la macro END_MESSAGE_MAP.

Entre estas dos llamadas a macros, hay una entrada para que este mapa de mensajes controle cada mensaje. Cada mensaje estándar de Windows tiene una macro con el formato ON_WM_NOMBRE_DEL_MENSAJE que genera una entrada para ese mensaje.

Se ha definido una firma de función estándar para desempaquetar los parámetros de cada mensaje de Windows y proporcionar seguridad de tipos. Estas firmas se pueden encontrar en el archivo Afxwin.h en la declaración de CWnd. Cada una está marcada con la palabra clave afx_msg para facilitar la identificación.

Nota:

ClassWizard requiere que use la palabra clave afx_msg en las declaraciones de controlador de mapa de mensajes.

Estas firmas de función se han derivado mediante una convención simple. El nombre de la función siempre comienza por "On". Esto va seguido del nombre del mensaje de Windows sin el "WM_" y la primera letra de cada palabra en mayúsculas. El orden de los parámetros es wParam seguido de LOWORD(lParam) y luego HIWORD(lParam). No se pasan los parámetros no utilizados. Los manipuladores que se encapsulan en las clases MFC se convierten en punteros a los objetos de MFC adecuados. En el ejemplo siguiente, se muestra cómo controlar el mensaje WM_PAINT y hacer que se llame a la función CMyWnd::OnPaint:

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

La tabla del mapa de mensajes se debe definir fuera del ámbito de cualquier función o definición de clase. No se debe colocar en un bloque de "C" externo.

Nota:

ClassWizard modificará las entradas del mapa de mensajes que se encuentran entre el corchete de comentarios //{{ y //}}.

Mensajes de Windows definidos por el usuario

Los mensajes definidos por el usuario se pueden incluir en un mapa de mensajes mediante la macro ON_MESSAGE. Esta macro acepta un número de mensaje y un método con el formato:

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

En este ejemplo, se establece un controlador para un mensaje personalizado que tiene un identificador de mensaje de Windows derivado del estándar WM_USER base para los mensajes definidos por el usuario. En el ejemplo siguiente, se muestra cómo llamar a este controlador:

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

El intervalo de los mensajes definidos por el usuario que usan este enfoque debe estar en el intervalo de WM_USER hasta 0x7fff.

Nota:

ClassWizard no admite la entrada de rutinas de controlador ON_MESSAGE desde la interfaz de usuario de ClassWizard. Debe escribirlas manualmente desde el editor de Visual C++. ClassWizard analizará estas entradas y le permitirá examinarlas igual que cualquier otra entrada de mapa de mensajes.

Mensajes de Windows registrados

La función RegisterWindowMessage se usa para definir un nuevo mensaje de ventana que se garantiza que sea único en todo el sistema. La macro ON_REGISTERED_MESSAGE se usa para controlar estos mensajes. Esta macro acepta un nombre de una variable UINT NEAR que contiene el identificador de mensaje de Windows registrado. Por ejemplo

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 variable del identificador de mensaje de Windows registrado (WM_FIND, en este ejemplo) debe ser una variable NEAR debido a la forma en que se implementa ON_REGISTERED_MESSAGE.

El intervalo de los mensajes definidos por el usuario que usan este enfoque debe estar en el intervalo 0xC000 hasta 0xFFFF.

Nota:

ClassWizard no admite la entrada de rutinas de controlador ON_REGISTERED_MESSAGE desde la interfaz de usuario de ClassWizard. Debe escribirlas manualmente desde el editor de texto. ClassWizard analizará estas entradas y le permitirá examinarlas igual que cualquier otra entrada de mapa de mensajes.

Mensajes de comando

Los mensajes de comandos de menús y aceleradores se controlan en mapas de mensajes con la macro ON_COMMAND. Esta macro acepta un identificador de comando y un método. Solo se controla mediante el método especificado en la entrada del mapa de mensajes el mensaje WM_COMMAND específico que tiene un elemento wParam igual al identificador de comando especificado. Las funciones miembro del controlador de comandos no toman parámetros y devuelven void. La macro tiene el siguiente formato:

ON_COMMAND(id, memberFxn)

Los mensajes de actualización de comandos se enrutan mediante el mismo mecanismo, pero usan la macro ON_UPDATE_COMMAND_UI en su lugar. Las funciones miembro del controlador de actualización de comandos toman un único parámetro, un puntero a un objeto CCmdUI, y devuelven void. La macro tiene el formato:

ON_UPDATE_COMMAND_UI(id, memberFxn)

Los usuarios avanzados pueden usar la macro ON_COMMAND_EX, que es una forma extendida de controladores de mensajes de comando. La macro proporciona un superconjunto de la funcionalidad de ON_COMMAND. Las funciones miembro del controlador de comandos extendido toman un único parámetro, un valor UINT que contiene el identificador de comando, y devuelven un valor BOOL. El valor devuelto debe ser TRUE para indicar que se ha controlado el comando. De lo contrario, el enrutamiento continuará hacia otros objetos de destino de comando.

Ejemplos de estas formas:

  • Dentro de Resource.h (normalmente generado por Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Dentro de la declaración de clase

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Dentro de la definición del mapa de mensajes

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • En el archivo de implementación

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

Los usuarios avanzados pueden controlar un intervalo de comandos mediante un único controlador de comandos: ON_COMMAND_RANGE u ON_COMMAND_RANGE_EX. Consulte la documentación del producto para obtener más información sobre estas macros.

Nota:

ClassWizard admite la creación de controladores ON_COMMAND y ON_UPDATE_COMMAND_UI, pero no admite la creación de controladores ON_COMMAND_EX ni ON_COMMAND_RANGE. Sin embargo, ClassWizard analizará y le permitirá examinar las cuatro variantes del controlador de comandos.

Mensajes de notificación de controles

Los mensajes que se envían desde los controles secundarios a una ventana tienen cierta información adicional en su entrada de mapa de mensajes: el identificador del control. Solo se llama al controlador de mensajes especificado en una entrada de mapa de mensajes si se cumplen las condiciones siguientes:

  • El código de notificación del control (palabra alta de lParam), como BN_CLICKED, coincide con el código de notificación especificado en la entrada de mapa de mensajes.

  • El identificador del control (wParam) coincide con el identificador de control especificado en la entrada de mapa de mensajes.

Los mensajes de notificación de control personalizados pueden usar la macro ON_CONTROL para definir una entrada de mapa de mensajes con un código de notificación personalizado. La macro tiene el formato:

ON_CONTROL(wNotificationCode, id, memberFxn)

Para un uso avanzado, se puede usar ON_CONTROL_RANGE para controlar una notificación de control específica de un intervalo de controles con el mismo controlador.

Nota:

ClassWizard no admite la creación de un controlador ON_CONTROL ni ON_CONTROL_RANGE en la interfaz de usuario. Debe escribirlos manualmente con el editor de texto. ClassWizard analizará estas entradas y le permitirá examinarlas igual que cualquier otra entrada de mapa de mensajes.

Los controles comunes de Windows usan WM_NOTIFY, más eficaz para las notificaciones de controles complejas. Esta versión de MFC admite directamente este nuevo mensaje mediante las macros ON_NOTIFY y ON_NOTIFY_RANGE. Consulte la documentación del producto para obtener más información sobre estas macros.

Consulte también

Notas técnicas por número
Notas técnicas por categoría