- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
Теперь рассмотрим, как VCL обрабатывает извлеченные из очереди сообщения. Как уже было сказано ранее, для каждого класса формы VCL регистрирует одноименный оконный класс, а все окна, принадлежащие одному оконному классу, имеют общую оконную процедуру. С другой стороны, логика работы VCL требует, чтобы события обрабатывались тем экземпляром oбъекта, который инкапсулирует окно-адресат. Таким образом, возникает вопрос о том, как передать сообщение заданному экземпляру класса VCL. VCL решает эту задачу следующим образом. Модуль Classes содержит недокументированную функцию MakeObjectInstance, описанную так:
type TWndMethod = procedure(var Message: TMessage) of object;
function MakeObjectInstance(Method: TWndMethod): Pointer;
Тип TMessage хранит информацию о сообщении. Все методы VCL-компонентов, связанные с обработкой сообщения, используют этот тип (чуть позже мы рассмотрим его более подробно).
Функция MakeObjectInstance динамически формирует новую оконную процедуру и возвращает указатель на нее (следовательно, любое VCL-приложение содержит самомодифицирующийся код). Задача этой динамически созданной процедуры — передать управление тому методу, который был указан при вызове MakeObjectInstance (таким образом, различные оконные процедуры, сформированные этой функцией, отличаются только тем, метод MainWndProc какого экземпляра класса они вызывают).
Каждый экземпляр оконного компонента создает свою оконную процедуру, которая передает обработку сообщения его методу MainWndProc. Указатель на эту процедуру записывается в поле FObjectInstance. Как мы уже говорили в предыдущем разделе, при регистрации оконного класса в качестве оконной процедуры указывается InitWndProc, которая при получении первого сообщения создает подкласс, и оконной процедурой назначается та, указатель на которую хранится в поле FObjectInstance, т.е. функция, созданная с помощью MakeObjectInstance (см. листинг 1.12). Таким образом, каждый экземпляр получает свою оконную процедуру, а обработку сообщения начинает метод MainWndProc.
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. Описание типа TMessageTMessage = 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. Тип TWMNCMouseMoveTWMNCMouseMove = packed record
Msg: Cardinal;
HitTest: LongInt;
XCursor: SmallInt;
YCursor: SmallInt;
Result: LongInt;
end;
Параметр WParam переименован в HitTest, что лучше отражает его смысл в данном случае, а параметр LParam разбит на две 16-разрядных части: XCursor и YCursor.
Параметр метода для обработки сообщения имеет тип, соответствующий обрабатываемому сообщению (при необходимости можно описать свой тип), или тип TMessage. Таким образом, обработчик сообщения WM_MOUSEMOVE будет выглядеть так, как показано в листинге 1.16.
Листинг 1.16. Объявление и реализация метода для обработки сообщения WM_MOUSEMOVEtype
TSomeForm = class(TForm)
...............

