Григорьев Антон Борисович
Шрифт:
MainWndProc
— это невиртуальный метод, обеспечивающий решение технических вопросов: удаление "мусора", оставшегося при обработке сообщения и обработку исключений. Собственно обработку сообщения он передает методу, на который указывает свойство WindowProc
. Это свойство имеет тип TWndMethod
и по умолчанию указывает на виртуальный метод WndProc
. Таким образом, если разработчик не изменял значения свойства WindowProc
, обработкой сообщения занимается WndProc
. Метод
WndProc
обрабатывает только те сообщения, которые должны быть обработаны специальным образом, чтобы поддержать функциональность VCL. Особым образом метод WndProc
обрабатывает сообщения от мыши: он следит, в границы какого визуального компонента попадают координаты "мышиных" сообщений, и если этот компонент отличается от того, в чью область попало предыдущее сообщение, компоненту из предыдущего сообщения дается команда обработать сообщение CM_MOUSELEAVE
, а новому — сообщение CM_MOUSENTER
. Это обеспечивает реакцию визуальных компонентов на приход и уход мыши (в частности, генерирование событий OnMouseEnter
и OnMouseExit
). Необходимость реализации такого способа отслеживания прихода и ухода мыши вместо использования системных сообщений WM_MOUSEHOVER
и WM_MOUSELEAVE
связана с тем, что системные сообщения пригодны только для работы с окнами, а VCL отслеживает приход и уход мыши и на неоконные визуальные компоненты. Впрочем, WM_MOUSELEAVE
в WndProc
тоже служит дополнительным средством проверки ухода мыши. Примечание
Описанный здесь способ отслеживание ухода и прихода мыши реализован, начиная с BDS 2006. В более ранних версиях Delphi за это отвечал метод
Application.Idle
, который, как мы помним, вызывается только тогда когда в очереди нет сообщений. Из-за этого иногда (например, при быстром движении мышью) события ухода и прихода мыши пропускались, нарушая логику работы программы. Поэтому в BDS 2006 способ контроля прихода и ухода мыши был изменен, и ответственность за это возложена на метод TWinControl.WndProc
. Это позволило избавиться от одного недостатка — потери событий, но породило другой: теперь перехват и самостоятельная обработка "мышиных" сообщений до того, как это сделает метод WndProc
, может привести к потере возможности отслеживания прихода и ухода мыши. Впрочем, эта проблема проявляется только при выполнении программистом определенных осмысленных действий по внедрению кода в оконную процедуру, поэтому она гораздо менее серьезна, чем та от которой удалось избавиться. События мыши метод
WndProc
диспетчеризует самостоятельно, без помощи функции DispatchMessage
. Это связано с тем, что DispatchMessage
передаёт сообщение тому оконному компоненту, которому оно предназначено с точки зрения системы. Однако с точки зрения VCL этот компонент может являться родителем для неоконных визуальных компонентов, и если сообщение от мыши связано с их областью, то оно должно обрабатываться соответствующим неоконным компонентом, а не его оконным родителем. DispatchMessage
ничего о неоконных компонентах не "знает" и не может передать им сообщения, поэтому разработчикам VCL пришлось реализовывать свой способ. Те сообщения, которые метод WndProc
не обрабатывает самостоятельно (а их — подавляющее большинство), он передает в метод Dispatch, который объявлен и реализован в классе TObject
. На первый взгляд может показаться странным, что в самом базовом классе реализована функциональность, использующаяся только в визуальных компонентах. Эта странность объясняется тем, что разработчики Delphi встроили поддержку обработки сообщений непосредственно в язык. Методы класса, описанные с директивой message, служат специально для обработки сообщений. Синтаксис описания такого метода следующий: procedure <Name>(var Message: <TMsgType>); message <MsgNumber>;
<MsgNumber>
— это номер сообщения, для обработки которого предназначен метод. Имя метода может быть любым, но традиционно оно совпадает с именем константы сообщения за исключением того, что в нем выбран более удобный регистр символов и отсутствует символ "_" (например, метод для обработки WM_SIZE
будет называться WMSize
). В качестве типа параметра
<TMsgType>
компилятор разрешает любой тип, но на практике имеет смысл только использование типа TMessage
или "совместимого" с ним. Тип TMessage
описан в листинге 1.14. Листинг 1.14. Описание типа
TMessage
TMessage = packed record
Msg: Cardinal;
case Integer of
0: (
WParam: LongInt;
LParam: LongInt;
Result: LongInt);
1: (
WParamLo: Word;
WParamHi: Word;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;
Поле Msg содержит номер сообщения, поля
WParam
и LParam
— значение одноименных параметров сообщения. Поле Result
— выходное: метод, осуществляющий окончательную обработку сообщения, заносит в него то значение, которое должна вернуть оконная процедура. Поля с суффиксами Lo
и Hi
позволяют обращаться отдельно к младшему и старшему словам соответствующих полей, что может быть очень полезно, когда эти параметры содержат пару 16-разрядных значений. Например, у сообщения WM_MOUSEREMOVE
младшее слово параметра LParam
содержит X-координату мыши, старшее — Y-координату. В случае обработки этого сообщения поле LParamLo
будет содержать X-координату, LParamHi
— Y-координату. "Совместимыми" с
TMessage
можно назвать структуры, которые имеют такой же размер, а также параметр Msg
, задающий сообщение. Эти структуры учитывают специфику конкретного сообщения. Их имена образуются из имени сообщения путем отбрасывания символа и добавления префикса T
. Для уже упоминавшегося сообщения WM_MOUSEMOVE
соответствующий тип выглядит, как показано в листинге 1.15. Листинг 1.15. Тип
TWMNCMouseMove