Если окно не имеет ни родителя, ни владельца в терминах системы (такие окна называютсяWS_EX_APPWINDOW
). Обычно в приложении одно окно главного уровня, и оно играет роль главного окна этого приложения, хотя система не запрещает приложению создавать несколько окон верхнего уровня (примеры — Internet Explorer, Microsoft Word). Разработчики VCL пошли по другому пути: окно верхнего уровня, ответственное за появление кнопки на панели задач, создается объектом Application
. Дескриптор этого окна хранится в свойстве Application.Handle
, а само оно невидимо, т.к. имеет нулевые размеры. Как и любое другое, это окно имеет оконную процедуру и может обрабатывать сообщения. Главная форма — это отдельное окно, не имеющее, с формальной точки зрения, никакого отношения к кнопке на панели задач. Видимость связи между этой кнопкой и главной формой обеспечивается взаимодействием объекта Application
и объекта главной формы внутри VCL. Таким образом, даже простейшее VCL-приложение создает два окна: невидимое окно объекта Application и окно главной формы. Окно, создаваемое объектом Application
, мы будем называтьParent
, в том числе и главной формы.
При обработке сообщений VCL решает две задачи: выборка сообщений из очереди и передача сообщения конкретному компоненту. Рассмотрим сначала первую задачу.
Выборкой сообщений из очереди занимается объект Application
, непосредственно за извлечение и диспетчеризацию сообщения отвечает его метод ProcessMessage
(листинг 1.13).
Листинг 1.13. Метод TApplication.ProcessMessage
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Unicode: Boolean;
Handled: Boolean;
MsgExists: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
begin
Unicode := (Msg.hwnd <> 0) and IsWindowUnicode(Msg.hwnd);
if Unicode then MsgExists := PeekMessageW(Msg, 0, 0, 0, PM_REMOVE)
else MsgExists := PeekMessage(Msg, 0, 0, 0, PM_REMOVE);
if not MsgExists then Exit;
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsPreProcessMessage(Msg) and not IsHintMsg(Msg) and not Handled and
not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
if Unicode then DispatchMessageW(Msg);
else DispatchMessage(Msg);
end;
end else FTerminate := True;
end;
end;
В этом коде отдельного комментария требует то, как используется функция PeekMessage
. Сначала эта функция вызывается с параметром PM_NOREMOVE
, — так выполняется проверка условия, что в очереди присутствует сообщение, а также выясняется, для какого окна предназначено первое сообщение в очереди. Само сообщение при этом остается в очереди. С помощью функции IsWindowUnicode
производится проверка, использует ли окно-адресат кодировку ANSI или Unicode, и затем, в зависимости от этого, сообщение извлекается либо функцией PeekMessage
, либо ее Unicode-аналогом PeekMessageW
(о Unicode-аналогах функций см. DispatchMessage
, либо ее Unicode-аналог DispatchMessageW
.
Если метод ProcessMessage
с помощью PeekMessage
извлекает из очереди сообщение WM_QUIT
, то он устанавливает в True
поле FTerminate
и завершает свою работу. Обработка всех остальных сообщений, извлеченных из очереди состоит из следующих основных этапов (см. рис. 1.6):
1. Если назначен обработчик Application.OnMessage
, сообщение передается ему. В этом обработчике можно установить параметр-переменную Handle
в True
, что означает, что сообщение не нуждается в дополнительной обработке.