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

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

Жанры

О чём не пишут в книгах по 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

INDIGO
8. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
6.13
рейтинг книги
На границе империй. Том 7. Часть 2

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

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

Песец всегда прав

Видум Инди
6. Под знаком Песца
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Песец всегда прав

Газлайтер. Том 4

Володин Григорий
4. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 4

Последний Герой. Том 1

Дамиров Рафаэль
1. Последний герой
Фантастика:
попаданцы
альтернативная история
фантастика: прочее
5.00
рейтинг книги
Последний Герой. Том 1

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

Андрей Мельник
7. Граф Берестьев
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Законы Рода. Том 7

Государь

Мазин Александр Владимирович
7. Варяг
Фантастика:
альтернативная история
8.93
рейтинг книги
Государь

Шайтан Иван 5

Тен Эдуард
5. Шайтан Иван
Фантастика:
попаданцы
альтернативная история
историческое фэнтези
5.00
рейтинг книги
Шайтан Иван 5

Предопределение

Осадчук Алексей Витальевич
9. Последняя жизнь
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Предопределение

Низший - Инфериор. Компиляция. Книги 1-19

Михайлов Дем Алексеевич
Фантастика 2023. Компиляция
Фантастика:
боевая фантастика
5.00
рейтинг книги
Низший - Инфериор. Компиляция. Книги 1-19

Попаданка. Финал

Ахминеева Нина
4. Двойная звезда
Фантастика:
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Попаданка. Финал

Кодекс Охотника. Книга IX

Винокуров Юрий
9. Кодекс Охотника
Фантастика:
боевая фантастика
городское фэнтези
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга IX

Санек

Седой Василий
1. Санек
Фантастика:
попаданцы
альтернативная история
4.00
рейтинг книги
Санек

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

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