TN006. Схемы сообщений

В этой заметке описывается объект карты сообщений MFC.

Проблема

Microsoft Windows реализует виртуальные функции в классах окон, использующих его средство обмена сообщениями. Из-за большого количества сообщений, предоставляя отдельную виртуальную функцию для каждого сообщения Windows, будет создавать запретительно большую vtable.

Так как количество системных сообщений Windows изменяется с течением времени, и поскольку приложения могут определять собственные сообщения Windows, карты сообщений предоставляют уровень косвенного обращения, который предотвращает нарушение существующего кода интерфейсом.

Обзор

MFC предоставляет альтернативу инструкции switch, которая использовалась в традиционных программах под управлением Windows для обработки сообщений, отправленных в окно. Сопоставление сообщений с методами можно определить таким образом, чтобы при получении сообщения окном соответствующий метод вызывается автоматически. Это средство сопоставления сообщений предназначено для напоминать виртуальные функции, но имеет дополнительные преимущества, недоступные для виртуальных функций C++.

Определение карты сообщений

Макрос DECLARE_MESSAGE_MAP объявляет три члена для класса.

  • Частный массив записей AFX_MSGMAP_ENTRY с именем _messageEntries.

  • Защищенная структура AFX_MSGMAP с именем messageMap , указывающая на массив _messageEntries .

  • Защищенная виртуальная функция, которая GetMessageMap возвращает адрес messageMap.

Этот макрос должен быть помещен в объявление любого класса с помощью карт сообщений. По соглашению он находится в конце объявления класса. Например:

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

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

    DECLARE_MESSAGE_MAP()
};

Это формат, созданный AppWizard и ClassWizard при создании новых классов. Скобки //{{ и //}} необходимы для ClassWizard.

Таблица карты сообщений определяется с помощью набора макросов, расширяющихся до записей карты сообщений. Таблица начинается с вызова макроса BEGIN_MESSAGE_MAP , который определяет класс, обрабатываемый картой сообщений, и родительским классом, в который передаются необработанные сообщения. Таблица заканчивается вызовом макроса END_MESSAGE_MAP .

Между этими двумя вызовами макросов является запись для каждого сообщения, которое будет обрабатываться этой картой сообщений. Каждое стандартное сообщение Windows содержит макрос формы ON_WM_MESSAGE_NAME , которая создает запись для этого сообщения.

Стандартная сигнатура функции определена для распаковки параметров каждого сообщения Windows и обеспечения безопасности типов. Эти подписи можно найти в файле Afxwin.h в объявлении CWnd. Каждая из них помечена ключевое слово afx_msg для простой идентификации.

Примечание.

КлассWizard требует использования afx_msg ключевое слово в объявлениях обработчика карты сообщений.

Эти подписи функций были производными с помощью простого соглашения. Имя функции всегда начинается с "On". За ним следует имя сообщения Windows с удалением "WM_" и первой буквой каждого слова с прописной буквой. Порядок параметров — wParam, за которым следует LOWORD(lParam), а затем HIWORD(lParam). Неиспользуемые параметры не передаются. Все дескрипторы, которые упаковываются классами MFC, преобразуются в указатели на соответствующие объекты MFC. В следующем примере показано, как обрабатывать сообщение WM_PAINT и вызывать CMyWnd::OnPaint функцию:

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

Таблица карты сообщений должна быть определена вне область любого определения функции или класса. Он не должен быть помещен в экстерн "C" блок.

Примечание.

ClassWizard изменит записи карты сообщений, происходящие между скобками комментариев //{и //}}.

Определяемые пользователем сообщения Windows

Определяемые пользователем сообщения могут быть включены в карту сообщений с помощью макроса ON_MESSAGE . Этот макрос принимает номер сообщения и метод формы:

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

В этом примере мы устанавливаем обработчик пользовательского сообщения с идентификатором сообщения Windows, производным от стандартной WM_USER базы для определяемых пользователем сообщений. В следующем примере показано, как вызвать этот обработчик:

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

Диапазон определяемых пользователем сообщений, использующих этот подход, должен находиться в диапазоне WM_USER для 0x7fff.

Примечание.

ClassWizard не поддерживает ввод подпрограмм обработчика ON_MESSAGE из пользовательского интерфейса ClassWizard. Их необходимо ввести вручную из редактора Visual C++. ClassWizard анализирует эти записи и позволяет просматривать их так же, как и любые другие записи карты сообщений.

Зарегистрированные сообщения Windows

Функция RegisterWindowMessage используется для определения нового сообщения окна, которое гарантированно будет уникальным во всей системе. Макрос ON_REGISTERED_MESSAGE используется для обработки этих сообщений. Этот макрос принимает имя переменной UINT NEAR , содержащей идентификатор зарегистрированного сообщения windows. Например.

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

Зарегистрированная переменная идентификатора сообщения Windows (WM_FIND в этом примере) должна быть переменной NEAR из-за способа реализации ON_REGISTERED_MESSAGE.

Диапазон определяемых пользователем сообщений, использующих этот подход, будет находиться в диапазоне 0xC000 0xFFFF.

Примечание.

ClassWizard не поддерживает ввод подпрограмм обработчика ON_REGISTERED_MESSAGE из пользовательского интерфейса ClassWizard. Их необходимо ввести вручную из текстового редактора. ClassWizard анализирует эти записи и позволяет просматривать их так же, как и любые другие записи карты сообщений.

Командные сообщения

Сообщения команд из меню и акселераторов обрабатываются в картах сообщений с помощью макроса ON_COMMAND. Этот макрос принимает идентификатор команды и метод. Только определенное сообщение WM_COMMAND, которое имеет wParam , равное указанному идентификатору команды, обрабатывается методом, указанным в записи карты сообщений. Функции-члены обработчика команд не принимают параметров и возвращаются void. Макрос имеет следующую форму:

ON_COMMAND(id, memberFxn)

Сообщения об обновлении команд направляются через тот же механизм, но вместо этого используйте макрос ON_UPDATE_COMMAND_UI. Функции-члены обработчика обновления команд принимают один параметр, указатель на объект CCmdUI и возвращаются void. Макрос имеет форму

ON_UPDATE_COMMAND_UI(id, memberFxn)

Расширенные пользователи могут использовать макрос ON_COMMAND_EX, который является расширенной формой обработчиков сообщений команд. Макрос предоставляет супермножество функций ON_COMMAND. Расширенные функции-обработчика команд принимают один параметр, UINT, содержащий идентификатор команды, и возвращает boOL. Возвращаемое значение должно иметь значение TRUE , чтобы указать, что команда была обработана. В противном случае маршрутизация продолжится к другим целевым объектам команды.

Примеры этих форм:

  • Внутри Resource.h (обычно создается Visual C++)

    #define    ID_MYCMD      100
    #define    ID_COMPLEX    101
    
  • Внутри объявления класса

    afx_msg void OnMyCommand();
    afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
    afx_msg BOOL OnComplexCommand(UINT nID);
    
  • Внутри определения карты сообщений

    ON_COMMAND(ID_MYCMD, OnMyCommand)
    ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand)
    ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
    
  • В файле реализации

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

Расширенные пользователи могут обрабатывать диапазон команд с помощью одного обработчика команд: ON_COMMAND_RANGE или ON_COMMAND_RANGE_EX. Дополнительные сведения об этих макросах см. в документации по продукту.

Примечание.

ClassWizard поддерживает создание обработчиков ON_COMMAND и ON_UPDATE_COMMAND_UI, но не поддерживает создание ON_COMMAND_EX или обработчиков ON_COMMAND_RANGE. Однако мастер классов анализирует и позволяет просматривать все четыре варианта обработчика команд.

Управление сообщениями уведомлений

Сообщения, отправляемые из дочерних элементов управления в окно, содержат дополнительную информацию в записи карты сообщений: идентификатор элемента управления. Обработчик сообщений, указанный в записи карты сообщений, вызывается только в том случае, если выполняются следующие условия:

  • Код уведомления элемента управления (высокое слово lParam), например BN_CLICKED, соответствует коду уведомления, указанному в записи карты сообщений.

  • Идентификатор элемента управления (wParam) соответствует идентификатору элемента управления, указанному в записи карты сообщений.

Сообщения уведомления пользовательского элемента управления могут использовать макрос ON_CONTROL для определения записи карты сообщений с пользовательским кодом уведомления. Этот макрос имеет форму

ON_CONTROL(wNotificationCode, id, memberFxn)

Для расширенного использования ON_CONTROL_RANGE можно использовать для обработки определенного уведомления элемента управления из диапазона элементов управления с тем же обработчиком.

Примечание.

ClassWizard не поддерживает создание обработчика ON_CONTROL или ON_CONTROL_RANGE в пользовательском интерфейсе. Их необходимо ввести вручную с помощью текстового редактора. ClassWizard анализирует эти записи и позволяет просматривать их так же, как и любые другие записи карты сообщений.

Общие элементы управления Windows используют более мощные WM_NOTIFY для сложных уведомлений элементов управления. Эта версия MFC имеет прямую поддержку этого нового сообщения с помощью ON_NOTIFY и макросов ON_NOTIFY_RANGE. Дополнительные сведения об этих макросах см. в документации по продукту.

См. также

Технические примечания по номеру
Технические примечания по категории