Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

Функции

DialogВохХХХХ
создают диалоговое окно и сразу же показывают его в модальном режиме. Данные функции завершают свое выполнение только тогда, когда модальное окно будет закрыто. Внутри модальных функций организуется собственная петля сообщений. Все прочие окна на время показа модального диалога запрещаются (как если бы для них была вызвана функция
EnableWindow
с параметром
FALSE
), т.е. перестают реагировать на сообщения от мыши и клавиатуры. При этом они сохраняют способность реагировать на другие сообщения, благодаря чему могут, например, обновлять свое содержимое по таймеру (в справке написано, что ничто не мешает программисту вставить в диалоговую процедуру вызов функций, разрешающих запрещенные системой окна, но
при этом теряется смысл модальных диалогов). Если в очереди нет сообщений, модальная петля посылает родительскому окну диалога сообщение
WM_ENTERIDLE
, обработка которого позволяет этому окну выполнять фоновые действия. Разумеется, что обработчик
WM_ENTERIDLE
не должен выполняться слишком долго, иначе модальное окно зависнет. Обычно окно использует оконную процедуру, которая задана при создании соответствующего оконного класса. Однако допускается создание так называемых подклассов — переопределение оконной процедуры после того, как окно создано. Это переопределение касается только заданного окна и не оказывает влияния на остальные окна, принадлежащие данному оконному классу. Осуществляется оно с помощью функции
SetWindowLong
с параметром
GWL_WNDPROC
(другие значения этого параметра позволяют менять другие свойства окна, такие как стиль и расширенный сталь). Изменять оконную процедуру можно только у окон, созданных самим процессом.

Новая оконная процедура, которая устанавливается при создании подкласса, все необработанные сообщения должна передавать не функции

DefWindowProc
, а той оконной процедуре, которая была установлена ранее.
SetWindowLong
при изменении оконной процедуры возвращает дескриптор старой процедуры (этот же дескриптор можно получить, заранее вызвав функцию
GetWindowLong
с аргументом
GWL_WINDOWPROC
). Обычно значение дескриптора численно совпадает с адресом старой оконной процедуры, поэтому в некоторых источниках можно встретить рекомендации использовать этот дескриптор непосредственно как указатель процедурного типа. И это даже будет работать для оконных классов, созданных самой программой. Но безопаснее все же вызов старой оконной процедуры реализовать с помощью системной функции
CallWindowProc
, предоставив ей "разбираться", является ли дескриптор указателем.

В качестве примера рассмотрим создание подкласса для некоторого окна, дескриптор которого содержится в переменной

Wnd
. Пусть нам потребовалось для этого окна нестандартным образом обрабатывать сообщение
WM_KILLFOCUS
.

Тогда код новой оконной процедуры и код ее установки будет выглядеть так, как показано в листинге 1.7.

Листинг 1.7. Создание подкласса для особой обработки сообщения
WM_KILLPFOCUS

var

 OldWndProc: TFNWndProc;

function NewWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM) : LRESULT; stdcall;

begin

 if Msg = WM_KILLFOCUS then

// Обработка события

 else

Result := CallWindowProc(OldWndProc, hWnd, Msg, wParam, lParam);

end;

...

// Установка новой оконной процедуры окну Wnd

OldWndProc := TFNWndProc(SetWindowLong(Wnd, GWL_WNDPROC, Longint(@NewWindowProc)));

...

Примечание

MSDN называет функции

GetWindowLong
и
SetWindowLong
устаревшими и рекомендует использовать вместо них
GetWindowLongPtr
и
SetWindowLongPtr
, совместимые с 64-разрядными версиями Windows. Однако до 2007-й
версии Delphi включительно эти функции отсутствуют в модуле Windows, и при необходимости их следует импортировать самостоятельно.

Переопределять оконную процедуру с помощью

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

1.1.7. Создание окон средствами VCL

Теперь поговорим о том, как в VCL создаются окна. Речь здесь будет идти не о написании кода для создания окна с помощью VCL (предполагается, что читатель это и так знает), а о том, какие функции API и в какой момент вызывает VCL при создании окна.

Если смотреть код методов класса

TWinControl
, которые вызываются при создании и отображении окна, то найти там то место, когда окно создается, удается не сразу. На первый взгляд все выглядит так, будто этот код вообще не имеет отношения к созданию окна, как будто оно создается где-то совсем в другом месте, а
TWinControl
получает уже готовый дескриптор. На самом деле окно создает, конечно же, сам
TWinControl
, а спрятано его создание в свойстве
Handle
. Метод
GetHandle
, который возвращает значение свойства
Handle
, выглядит следующим образом (листинг 1.8).

Листинг 1.8. Реализация метода
TWinControl.GetHandle

procedure TWinControl.HandleNeeded;

begin

 if FHandle = 0 then

 begin

if Parent <> nil then Parent.HandleNeeded;

CreateHandle;

 end;

end;

function TWinControl.GetHandle: HWnd;

begin

 HandleNeeded;

 Result := FHandle;

end;

При каждом обращении к свойству

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

Метод

CreateHandle
, который вызывается из
HandleNeeded
, выполняет непосредственно лишь несколько вспомогательных операций, а для создания окна вызывает еще один метод —
CreateWnd
(листинг 1.9).

Листинг 1.9. Реализация метода
CreateWnd

procedure TWndControl.CreateWnd;

var

 Params: TCreateParams;

 TempClass: TWndClass;

 ClassRegistered: Boolean;

begin

 CreateParams(Params);

 with Params do

 begin

if (WndParent = 0) end (Style and WS_CHILD <> 0) then

if (Owner <> nil) end (csReading in Owner.ComponentState) and (Owner is TWinControl) then

WndParent TWinControl(Owner).Handle

else

raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);

FDefWndProc := WindowClass.lpfnWndProc;

ClassRegistered := GetClassInfo(WindowClass.hInstance, WinClassName, TempClass);

if not ClassRegistered or (TempClass.lpfnWndProc <> @InitWndProc) then

Поделиться:
Популярные книги

Мастер Разума III

Кронос Александр
3. Мастер Разума
Фантастика:
героическая фантастика
попаданцы
аниме
5.25
рейтинг книги
Мастер Разума III

Часовое имя

Щерба Наталья Васильевна
4. Часодеи
Детские:
детская фантастика
9.56
рейтинг книги
Часовое имя

Печать мастера

Лисина Александра
6. Гибрид
Фантастика:
попаданцы
технофэнтези
аниме
фэнтези
6.00
рейтинг книги
Печать мастера

Идеальный мир для Лекаря

Сапфир Олег
1. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря

Кротовский, не начинайте

Парсиев Дмитрий
2. РОС: Изнанка Империи
Фантастика:
городское фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Кротовский, не начинайте

Эволюция мага

Лисина Александра
2. Гибрид
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эволюция мага

Прорвемся, опера! Книга 3

Киров Никита
3. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 3

Демон

Парсиев Дмитрий
2. История одного эволюционера
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Демон

Прорвемся, опера! Книга 2

Киров Никита
2. Опер
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Прорвемся, опера! Книга 2

#Бояръ-Аниме. Газлайтер. Том 11

Володин Григорий Григорьевич
11. История Телепата
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 11

Офицер

Земляной Андрей Борисович
1. Офицер
Фантастика:
боевая фантастика
7.21
рейтинг книги
Офицер

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

Сделай это со мной снова

Рам Янка
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Сделай это со мной снова

Злыднев Мир. Дилогия

Чекрыгин Егор
Злыднев мир
Фантастика:
фэнтези
7.67
рейтинг книги
Злыднев Мир. Дилогия