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

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

Жанры

О чём не пишут в книгах по 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
попадает правильная ссылка на создаваемый объект.

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

Адвокат Империи 3

Карелин Сергей Витальевич
3. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Адвокат Империи 3

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Дурная жена неверного дракона

Ганова Алиса
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Дурная жена неверного дракона

Вонгозеро

Вагнер Яна
1. Вонгозеро
Детективы:
триллеры
9.19
рейтинг книги
Вонгозеро

Ведьма Вильхельма

Шёпот Светлана
Любовные романы:
любовно-фантастические романы
8.67
рейтинг книги
Ведьма Вильхельма

Папина дочка

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

Законы Рода. Том 6

Flow Ascold
6. Граф Берестьев
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 6

Как я строил магическую империю 7

Зубов Константин
7. Как я строил магическую империю
Фантастика:
попаданцы
постапокалипсис
аниме
фантастика: прочее
5.00
рейтинг книги
Как я строил магическую империю 7

Лучший из худший 3

Дашко Дмитрий
3. Лучший из худших
Фантастика:
городское фэнтези
попаданцы
аниме
6.00
рейтинг книги
Лучший из худший 3

Штурмовик из будущего 3

Политов Дмитрий Валерьевич
3. Небо в огне
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Штурмовик из будущего 3

Последний попаданец 2

Зубов Константин
2. Последний попаданец
Фантастика:
юмористическая фантастика
попаданцы
рпг
7.50
рейтинг книги
Последний попаданец 2

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

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

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Вдова на выданье

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Вдова на выданье