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

Шрифт
Интервал

> 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

>  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


Рекомендуем почитать
Обратные вызовы в C++

В практике разработки ПО зачастую встает задача динамической модификации программного кода в зависимости от текущих или настраиваемых значений параметров. Для решения этой задачи широко используются обратные вызовы. В языке C++ обратные вызовы реализуются различными способами, и далеко не всегда очевидно, какой из них лучший для конкретной ситуации. В книге рассмотрены теоретические и практические аспекты организации обратных вызовов, проанализированы достоинства и недостатки различных реализаций, выработаны рекомендации по выбору в зависимости от требований к проектируемому ПО.


Теоретический минимум по Computer Science

Хватит тратить время на скучные академические фолианты! Изучение Computer Science может быть веселым и увлекательным занятием. Владстон Феррейра Фило знакомит нас с вычислительным мышлением, позволяющим решать любые сложные задачи. Научиться писать код просто — пара недель на курсах, и вы «программист», но чтобы стать профи, который будет востребован всегда и везде, нужны фундаментальные знания. Здесь вы найдете только самую важную информацию, которая необходима каждому разработчику и программисту каждый день. «Эта книга пригодится и для решения повседневных задач.


Идиомы и стили С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Человеческий фактор в программировании

Хорошее программное обеспечение создается людьми. Так же как и плохое. Именно поэтому основная тема этой книги — не аппаратное и не программное обеспечение, а человеческий фактор в программировании (peopleware). Первое издание «Constantine on Peopleware» признано классическим трудом в области информационных технологий. Новая книга Ларри Константина включает все 52 легендарные статьи из предыдущей книги и 25 новых эссе.Peopleware охватывает все аспекты, связанные с ролью людей в разработке программного обеспечения.


Единая система программной документации. Техническое задание. Требования к содержанию и оформлению

United system for program documentation. Technical specification for development. Requirements to contents and form of presentation Настоящий стандарт устанавливает порядок построения и оформления технического задания на разработку программы или программного изделия для вычислительных машин, комплексов и систем независимо от их назначения и области применения.Стандарт полностью соответствует СТ СЭВ 1627-79.Переиздание (Ноябрь 1987 г.) с Изменением № 1, утвержденным в июле 1981 г (ИУС 7-81)


Тонкости дизассемблирования

Очень часто под рукой не оказывается ни отладчика, ни дизассемблера, ни даже компилятора, чтобы набросать хотя бы примитивный трассировщик. Разумеется, что говорить о взломе современных защитных механизмов в таких условиях просто смешно, но что делать если жизнь заставляет?..