О чём не пишут в книгах по Delphi
Шрифт:
begin
if (ClassRegistered then
Windows.UnregisterClass(WinClassName, WindowClass.hInstance);
WindowClass.lpfnWndProc := InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then RaiseLastOSError;
end;
CreationControl := Self;
CreateWindowHandle(Params);
if FHandle = 0 then RaiseLastOSError;
if (GetWindowLong(FHandle, GWL_STYLE) and WS_CHILD <> 0) and (GetWindowLong(FHandle, GWL_ID) = 0) then
SetWindowLong(FHandle, GWL_ID, FHandle);
end;
StrDispose(FText);
FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then AdjustSize;
end;
Собственно
CreateWindowHandle
, который очень прост: он состоит из одного только вызова API-функции CreateWindowEx
с параметрами, значения которых берутся из полей записи Params
типа TCreateParams
(листинг 1.10) Листинг 1.10. Запись
TCreateParams
TCreateParams = record
Caption: PChar;
Style: WORD;
ExStyle: DWORD;
X, Y: Integer;
Width, Height: Integer;
WndParent: HWnd;
Param: Pointer;
WindowClass: TWndClass;
WinClassName: array[0..63] of Char;
end;
В записи
Params
хранятся параметры как окна, передаваемые в функцию WindowCreateEx
, так и оконного класса (поля WindowClass
и WndClassName
). Все поля инициализируются методом CreateParams
на основе значений свойств оконного компонента. Данный метод виртуальный и может быть перекрыт в наследниках, что бывает полезно, когда необходимо изменить стиль создаваемого окна. Например, добавив расширенный стиль WS_EX_CLIENTEDGE
(или, как вариант, WS_EX_STATICEDGE
), можно получить окно с необычной рамкой (листинг 1.11). Листинг 1.11. Перекрытие метода
CreateParams
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
// Вызов унаследованного метода заполнения всех полей
// записи Params
inherited CreateParams(Params);
// Добавляем флаг WS_EX_CLIENTEEDGE к расширенному стилю окна
Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE;
end;
Примечание
В разд. 1.1.4 мы говорили, что имя оконного класса, который VCL создает для оконного компонента, совпадает с именем класса этого компонента. Здесь мы видим, что на самом деле имя оконного класса можно сделать и другим, для этого достаточно изменить значение поля
Params.WinClassName
. Обратите внимание, что всем без исключения классам метод
CreateWnd
назначает одну и ту же оконную процедуру — InitWndProc
. Это является основой в обработке сообщений с помощью VCL, именно поэтому оконная процедура назначается не в методе CreateParams
, а в методе CreateWnd
, чтобы в наследниках нельзя было изменить это поведение (метод CreateWnd
тоже виртуальный, но при его переопределении имеет смысл только добавлять
Чтобы понять, как работает процедура
InitWndProc
, обратите внимание на еще одну особенность метода CreateWnd
: перед вызовом CreateWindowHandle
(т.е. непосредственно перед созданием окна) он записывает ссылку на текущий объект в глобальную переменную СreationСontrol
. Эта переменная затем используется процедурой InitWndProc
(листинг 1.12). Листинг 1.12. Оконная процедура
InitWndProc
function InitWndProc(HWindow: HWnd; Message, WParam, LParam: LongInt): LongInt;
begin
CreationControl.FHandle := HWindow;
SetWindowLong (HWindow, GWL_WNDPROC, LongInt(CreationControl.FObjectInstance));
if (GetWindowLong(HWindow, GWL_STYLE) and WS_CHILD <> 0) and (GetWindowLong(HWindow, GWL_ID) = 0) then
SetWindowLong(HWindow, GWL_ID, HWindow);
SetProp(HWindow, MakeIntAtom(ControlAtom), THandle(CreationControl));
SetProp(HWindow, MakeIntAtom(WindowAtom), THandle(CreationControl));
asm
PUSH LParam
PUSH WParam
PUSH Message
PUSH HWindow
MOV EAX, CreationControl
MOV CreationControl, 0
CALL [EAX].TWinControl.FObjectInstance
MOV Result, EAX
end;
end;
Примечание
Код функции
InitWndProc
в листинге 1.12 взят из Delphi 7. В более поздних версиях код включает в себя поддержку окон, работающих с кодировкой Unicode, поэтому там предусмотрен выбор между ANSI- и Unicode-вариантами функций API (подробнее об ANSI- и Unicode-вариантах см разд. 1.1.12). Такой код сложнее понять из-за этих дополнений. Кроме того, из листинга 1.12 убрано все, что относится к компиляции под LINUX, чтобы не засорять листинг. Из листинга 1.12 видно, что оконная процедура
InitWndProc
не обрабатывает сама никаких сообщений, а просто переназначает оконную процедуру у окна. Таким образом, InitWndProc
для каждого окна вызывается только один раз, чтобы переназначить оконную процедуру. Обработка того сообщения, которое привело к вызову InitWndProc
, тоже передается в эту новую процедуру (ассемблерная вставка в конце InitWndProc
делает именно это). При просмотре этого кода возникают два вопроса. Первый — зачем писать такую оконную процедуру, почему бы не назначить нужную процедуру обычным образом? Здесь все дело в том. что стандартными средствами оконная процедура назначается одна на весь оконный класс, в то время как по внутренней логике VCL каждый экземпляр компонента должен иметь свою собственную оконную процедуру. Добиться этого можно только порождением подкласса уже после создания окна. Указатель на свою уникальную оконную процедуру (откуда эта процедура берется и почему она должна быть уникальной, мы поговорим в следующем разделе) каждый экземпляр хранит в поле FObjectInstance
. Значение глобальной переменной CreationControl
присваивается, как мы помним, непосредственно перед созданием окна, а первое свое сообщение окно получает буквально в момент создания. Так как VCL — принципиально однонитевая библиотека, ситуация, когда другой код вклинивается между присваиванием значения переменной CreationControl
и вызовом InitWndProc
, невозможна, так что в InitWndProc
попадает правильная ссылка на создаваемый объект.
Поделиться:
Популярные книги
На границе тучи ходят хмуро...
1. Александр Агренев
Фантастика:
альтернативная история
9.28
рейтинг книги
Энфис. Книга 1
1. Эрра
Фантастика:
боевая фантастика
рпг
5.70
рейтинг книги
Я – Орк. Том 4
4. Я — Орк
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Совок-8
8. Совок
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Я снова не князь! Книга XVII
17. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Жена со скидкой, или Случайный брак
Любовные романы:
любовно-фантастические романы
8.15
рейтинг книги
Адепт: Обучение. Каникулы [СИ]
6. Совсем не герой
Фантастика:
фэнтези
попаданцы
9.15
рейтинг книги
Огненный князь 6
6. Багряный восход
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Как я строил магическую империю 2
2. Как я строил магическую империю
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Цеховик. Книга 1. Отрицание
1. Цеховик
Фантастика:
попаданцы
альтернативная история
5.75
рейтинг книги
Безымянный раб
1. Дорога домой
Фантастика:
фэнтези
9.31
рейтинг книги
Матабар. II
2. Матабар
Фантастика:
фэнтези
5.00
рейтинг книги
Виконт. Книга 2. Обретение силы
2. Псевдоним `Испанец`
Фантастика:
боевая фантастика
попаданцы
рпг
7.10
рейтинг книги
Первый пользователь. Книга 3
3. Первый пользователь
Фантастика:
боевая фантастика
рпг
5.00