Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой заметке описывается функция карты сообщений 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 для простой идентификации.
Замечание
ClassWizard требует, чтобы в объявлениях обработчика карты сообщений использовалось ключевое слово 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 Studio. 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 Studio)
#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. Дополнительные сведения об этих макросах см. в документации по продукту.
См. также
Технические примечания по номеру
Технические заметки по категориям