OpenGL в Delphi

Событие, сообщение, ссылка


С понятием "событие" знаком каждый программист, использующий Delphi. Термин "сообщение" напрямую в концепции Delphi не используется.
Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями (окнами) посредством посылки сигналов, называемых сообщениями.
Код, написанный в проекте Delphi как обработчик события OnCreate, выполняется при получении приложением сообщения WM_CREATE, сообщению WM_PAINT соответствует событие OnPaint и т. д.
Такие события - аналоги сообщений операционной системы - используют мнемонику, сходную с мнемоникой сообщений, т. e. сообщения начинаются с префикса "WM_" (Windows Message), a аналогичные события начинаются с префикса "On".
Для того чтобы операционная система могла различать окна для осуществления диалога с ними, все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый "ссылка на окно". Тип этой величины в Delphi - HWND (Handle WiNDow). Синонимом термина "ссылка" является дескриптор.
Ссылка на окно может использоваться не только операционной системой, но и приложениями для идентификации окна, с которым необходимо производить манипуляции.
Попробуем проиллюстрировать смысл ссылки на окно на несложном примере.
Откомпилируйте минимальное приложение Delphi и начните новый проект. Форму назовите Form2, разместите на ней кнопку. Обработчик события нажатия кнопки OnClick приведите к следующему виду (готовый проект располагается на дискете в подкаталоге Ex01 каталога Chapter1):

procedure TForm2.ButtonlClick(Sender: TObject);
var
H : HWND; // ссылка на окно
begin
H := FindWindow ('TForm1', 'Form1'); // ищем окно
If H <> 0 then ShowMessage ('Есть Form1!') // окно найдено
else ShowMessage ('Нет Form1!') // окно не найдено
end;

Теперь при нажатии кнопки выдается сообщение, открыто ли окно класса, зарегистрированного в операционной системе как 'TForml1', имеющее заголовок 'Form1'. Если одновременно запустить обе наши программы, то при нажатии кнопки будет выдано одно сообщение, а если окно с заголовком 'Form1' закрыть, то другое.
Здесь мы используем функцию FindWindow, возвращающую величину типа HWND - ссылку на найденное окно либо ноль, если такое окно не найдено. Аргументы функции - класс окна и его заголовок. Если заголовок искомого окна безразличен, вторым аргументом нужно задать nil.
Итак, ссылка на окно однозначно определяет окно. Свойство Handle формы и есть эта ссылка, а тип THandle в точности соответствует типу HWND, так что в предыдущем примере переменную Н можно описать как переменную типа THandle.
Рассмотрим подробнее некоторые выводы. Класс окна минимального приложения, созданного в Delphi, имеет значение 'TForm1', что полностью соответствует названию класса формы в проекте. Следовательно, то, как мы называем формы в проектах Delphi, имеет значение не только в период проектирования приложения, но и во время его работы. Начните новый проект, назовите форму каким-нибудь очень длинным именем и откомпилируйте проект. Сравните размер откомпилированного модуля с размером самого
первого проекта, и убедитесь, что он увеличился - вырос только за счет длинного имени класса.
Также очень важно уяснить, что, если вы собираетесь распространять какие-либо приложения, необходимо взять за правило называть формы отлично от значения, задаваемого Delphi по умолчанию. Лучше, если эти названия будут связаны по смыслу с работой вашего приложения. Так, например, головную форму в примерах этой книги я буду называть, как правило, frmGL.
Имея ссылку на окно, операционная система общается с ним путем посылки сообщений - сигналов о том, что произошло какое-либо событие, имеющее отношение именно к данному окну. Если окно имеет намерение отреагировать на событие, операционная система совместно с окном осуществляет эту реакцию.
Окно может и, не имея фокус, получать сообщения и реагировать на них. Проиллюстрируем это на примере.
Обработчик события OnMouseMove формы приведите к следующему виду (проект находится в подкаталоге Ех02):

procedure TForm2.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer) ;
begin
Caption := 'x=' + IntToStr (X) + ', y=' + IntToStr (Y) // X, Y -
// координаты курсора
end;

При движении курсора мыши в заголовке формы выводятся его координаты.
Запустите два экземпляра программы и обратите внимание, что окно, не имеющее фокус, т. е. неактивное, тоже реагирует на перемещение указателя по своей поверхности и выводит в заголовке текущие координаты курсора в своей системе координат.
Имея ссылку на окно, приложение может производить с ним любые (почти) действия путем посылки ему сообщений.
Изменим код обработки щелчка кнопки (проект из подкаталога Ех03):

procedure TForm2.ButtonlClick(Sender: TObject);
var
H : HWND;
begin
H := FindWindow ('TForml', ' Forml');
If H <> 0 then SendMessage (H, WM_CLOSE, 0, 0)//закрыть найденное окно
end;

Если имеется окно класса 'TForm1' с заголовком 'Form1', наше приложение посылает ему сообщение WM_CLOSE - пытается закрыть окно. Для посылки сообщения используем функцию операционной системы (функцию API) SendMessage. Функция postMessage имеет сходное назначение, но отличается тем, что не дожидается, пока посланное сообщение будет отработано. У этих функций четыре аргумента - ссылка на окно, которому посылаем сообщение, константа, соответствующая посылаемому сообщению, и два параметра сообщения, смысл которых определяется в каждом конкретном сообщении по-своему. Параметры сообщения называются wParam и lParam. При обработке сообщения WM_CLOSE эти значения никак не используются, поэтому здесь их можно задавать произвольно.
Заметим, что одновременно могут быть зарегистрированы несколько окон класса 'TForm1', и необходимо закрыть их все. Пока наше приложение закрывает окна поодиночке при каждом нажатии на кнопку. Автоматизировать процесс можно разными способами, простейший из них используется в проекте подкаталога Ex04 и заключается в том, что вызов Findwindow заключен в цикл, работающий до тех пор, пока значение переменной H не станет равным нулю:

procedure TForm2. ButtonlClick(Sender: TObject);
var
H: HWND;
begin
Repeat
H: = FindWindow ('TForm1', 'Form1');
If H <> 0 then SendMessage (H, WM_CLOSE, 0, 0)
Until H = 0;
end;

Ну а как работать с приложениями, класс окна которых не известен, поскольку у нас нет (и не может быть) их исходного кода?
Для решения подобных проблем служит утилита Ws32, поставляемая с Delphi.
Например, с помощью этой утилиты я выяснил, что класс окна главного окна среды Delphi имеет значение 'TAppBuilder'. Узнав это, я смог написать проект, где делается попытка закрыть именно это окно (находится в подкаталоге Ex05).
Каждый раз я говорю именно о попытке закрыть окно, потому что приложение, получающее сообщение WM_CLOSE, может и не закрыться сразу же. Например, среда Delphi или текстовый процессор перед закрытием переспрашивают пользователя о необходимости сохранения данных. Но ведет себя приложение точно так же, как если бы команда поступала от пользователя.
В качестве следующего упражнения рассмотрите проект, располагающийся в подкаталоге Ex06, где по нажатию кнопки минимизируется окно, соответствующее минимальному проекту Delphi.
Для того чтобы минимизировать окно, ему посылается сообщение WM_SYSCOMMAND, соответствующее действию пользователя "выбор системного меню окна". Третий параметр функции SendMessage для минимизации окна необходимо установить в значение SC_MINIMIZE.
Работа с функциями API, сообщения Windows - темы весьма объемные. Пока мы рассмотрели только самые простейшие действия - закрыть и минимизировать окно.
В заключение раздела, необходимо сказать, что ссылки, в зависимости от версии Delphi, соответствуют типам Integer или LongWord и описываются в модуле windows. pas.



Содержание раздела