О чём не пишут в книгах по 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
попадает правильная ссылка на создаваемый объект.
Поделиться:
Популярные книги
На границе империй. Том 7. Часть 2
8. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
6.13
рейтинг книги
Законы Рода. Том 6
6. Граф Берестьев
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Песец всегда прав
6. Под знаком Песца
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 4
4. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Последний Герой. Том 1
1. Последний герой
Фантастика:
попаданцы
альтернативная история
фантастика: прочее
5.00
рейтинг книги
Законы Рода. Том 7
7. Граф Берестьев
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Государь
7. Варяг
Фантастика:
альтернативная история
8.93
рейтинг книги
Шайтан Иван 5
5. Шайтан Иван
Фантастика:
попаданцы
альтернативная история
историческое фэнтези
5.00
рейтинг книги
Предопределение
9. Последняя жизнь
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Низший - Инфериор. Компиляция. Книги 1-19
Фантастика 2023. Компиляция
Фантастика:
боевая фантастика
5.00
рейтинг книги
Попаданка. Финал
4. Двойная звезда
Фантастика:
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга IX
9. Кодекс Охотника
Фантастика:
боевая фантастика
городское фэнтези
попаданцы
5.00
рейтинг книги
Санек
1. Санек
Фантастика:
попаданцы
альтернативная история
4.00
рейтинг книги
Как я строил магическую империю 7
7. Как я строил магическую империю
Фантастика:
попаданцы
постапокалипсис
аниме
фантастика: прочее
5.00