Сообщения Windows и их источники

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

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

Сообщения поименованы и, как правило, все идентификаторы сообщений Windows начинаются на WM_ (Windows Message).
Таким образом, все управляющие элементы и приложения Windows сообщаются посредством системы отправки сообщений. По форме сообщение представляет собой запись, конкретный вид которой зависит от типа сообщения. В отличие от событий Delphi, обработка сообщений обязательна.

Важно не только уметь обрабатывать поступающие сообщения, но и уметь создавать собственные.

Принцип работы системы сообщений Windows

Приложению посылается сообщение всякий раз, когда должно произойти какое-то изменение. Работающая в среде Windows программа фактически ничего не делает сама с экраном, принтером, прерываниями и т.п. Вместо этого она в ответ на сообщение вызывает соответствующие функции Windows или ждет нужного сообщения.

В Windows существует единственная система распределения сообщений — системная очередь сообщений (Message queue). При этом приложения имеют и свои собственные очереди. Каждое сообщение рано или поздно из системной очереди пересылается модулем User в очередь сообщений конкретной программы. Когда приложение получает сообщение, то оно фактически получает данные, упакованные в структуру вида:

PMsg=^TMsg; // Определена в модуле Windows Delphi
TMsg=Record
HWnd: HWND; //Дескриптор окна — получателя сообщения
Message: UINT; // Содержит константу номера сообщения
wParam: WPARAM; //Дополнительные данные сообщения
lParam: LPARAM; // Дополнительные данные сообщения
Time: DWORD; // Время обработки сообщения Windows
Pt: TPoint; // Координаты мыши при выдаче сообщения
End;

Таким образом, структура TMsg содержит сведения о том, какому окну какое сообщение адресовано, а также ряд дополнительных параметров.

После запуска программы каждое окно (формы, кнопки, поля ввода и т.п. — все это окна) регистрируется в Windows, получая значение дескриптора (Handle: THandle) — фактически число, которое в дальнейшем и используется для обращения к этому окну, в том числе и для обработки сообщения. Каждое окно имеет специальную процедуру для обработки сообщений.

Основная составляющая работы любого приложения, работающего под Windows, это цикл обработки сообщений (Message loop). Цикл сообщений приложения получает сообщение из очереди, передает его соответствующему окну приложения для обработки, затем получает из очереди следующее сообщение, вновь передает его, и так до окончания работы приложения.

Фактически сообщение посылается не приложению, а конкретному окну, принадлежащему тому или иному приложению. Далее сообщение обрабатывается. Подобные сообщения называются синхронными (synchronous messages). Существуют и асинхронные сообщения (asynchronous messages). Это сообщения, которые непосредственно передаются оконной процедуре, минуя очередь сообщений. К асинхронным относятся такие сообщения, как перерисовка, завершение работы программы, срабатывание таймера и ряд других.

В приложениях Delphi цикл обработки сообщений реализован в модуле Forms. В процессе работы приложения Delphi метод TApplication.Run периодически вызывает метод TApplication.HandleMessage, который просматривает очередь сообщений.

Таким образом, если очередь не пуста, то управление передается методу TApplication.ProcessMessage. Этот метод, если посмотреть на его код в блоке Forms, проверяет содержимое поля FOnMessage (фактически проверяет наличие обработчика у события OnMessage) и, если оно не пусто, то вызывает обработчик этого события, а если поле пусто (равно Nil), то вызывает функцию API DispatchMessage(Msg). Эта функция ищет нужное окно для поступившего сообщения и посылает ему сообщение в виде структуры TMsg.

Если же очередь сообщений пуста, то управление передается методу TApplication.Idle. Этот метод, в свою очередь, если посмотреть на его код в блоке Forms, проверяет содержимое поля FOnldle (фактически проверяет наличие обработчика у события Onldle) и, если оно не пусто, вызывается обработчик этого события — обработчик отсутствия сообщений, называемый также в литературе обработчиком фонового режима. Если же содержимое поля FOnldle равно Nil, то в программе ничего не происходит, и она находится в режиме ожидания поступления сообщений.

Помимо просмотра очереди сообщений приложение Delphi из структуры типа TMsg часть информации сообщения обрабатывает самостоятельно, а другую часть информации сообщения всегда преобразует в запись типа TMessage, которая содержит поле Result, позволяющее вернуть значение из процедуры обработки сообщения. Запись TMessage, включающая вариантную часть, определена в модуле Messages следующим образом:

PMessage=^TMessage;
TMessage=Record
Msg: Cardinal; // Идентификатор номера сообщения
Case Integer Of
0: (wParam, lParam, Result: Longlnt);
1: (wParamLo, wParamHi, lParamLo, lParamHi, ResultLo, ResultHi: Word); End;

Таким образом, обработка сообщений производится той или иной процедурой окна (Window procedure). При этом процедура каждого окна приложения объединяет в себе функции обработки всех типов сообщений для данного окна. Эта процедура представляет собой функцию обратного вызова, которая выполняет определенные действия в ответ на сообщения и обычно возвращает Windows некоторое значение после их обработки.

Сообщения, определяемые пользователем
Windows позволяет определять имена (идентификаторы) для номеров (индексов) новых сообщений. Справочная система Delphi рекомендует использовать для объявления пользовательских сообщений константу — WM_APP=$8000, которая также объявлена в модуле Messages.

Для объявления нового идентификатора надо в секции Const записать:

Const
WM_USER1=WM_APP; // Номер первого сообщения
WM_USER2=WM_APP+1; // Номер второго сообщения

Объявление обработчиков сообщений
Delphi предоставляет возможность объявлять методы обработчиками сообщений Windows (методами сообщений). Такие методы составляют подмножество динамических методов и объявляются директивой Message, за которой следует индекс (номер), указанный с помощью идентификатора сообщения.

Синтаксис объявления метода сообщений в Delphi:

Procedure <имя метода>(Var <параметр>[: <тип>]); Message WM_**;

Примечания:
• Методы сообщений обязательно должны быть процедурами.
• Они принимают единственный Var-параметр произвольного типа или
нетипизированный.
• Они принимают целочисленную константу (в диапазоне значений 1…49151) в качестве динамического индекса (номера сообщения) в соответствие с директивой Message.
• Имя метода не играет роли при обработке сообщений, поскольку при обращении используется только индекс сообщения, закрепленный за указанным идентификатором сообщения — WM**.
• Для перекрытия метода сообщений директива Override не используется. В этом случае нужно сохранить в описании нового метода директиву Message с тем же индексом метода, что и у перекрываемого метода.
• В обработчиках сообщений целесообразно вызывать метод-предок, указав Inherited, но без указания его имени — в этом особенность использования Inherited в сообщениях. Чтобы выполнить всю работу по обработке сообщений, за редким исключением, настоятельно рекомендуется вызывать предыдущий обработчик. Таким образом, собственный обработчик исполнит тот код, который заложен в логику работы приложения, а наследуемый обработчик выполнит все действия, которые требуется системе Windows.

Рекомендуемые правила для объявления собственных сообщений:
• именовать тип записи, начиная с буквы Т;
• объявлять метод Message в разделе Protected класса (т.е. эти методы
могут быть доступны только в потомках);
• объявлять метод Message после объявления идентификатора сообщения (WM_**), которое он обрабатывает.


Оставить комментарий





Статистика

Рейтинг@Mail.ru