Функции windows для delphi

Функции windows для delphi

© А. Тенцер
Статья была опубликована на сайте «КомпьютерПресс»

Одной из наиболее интересных особенностей Delphi является предоставление, наряду с высокоуровневыми функциями VCL, простого доступа к функциям Windows API. Программист в любой момент волен (в зависимости от стоящей перед ним задачи) выбрать для ее решения простые в использовании компоненты либо реализовать алгоритм, требующий компактности и быстродействия при помощи прямых вызовов API. Более того, как правило, можно без прекращения использования компонентов и визуального программирования нанести несколько штрихов при помощи API и добиться максимальной точности решения задачи и быстродействия.

Windows — окна и сообщения

Само название операционной системы Windows говорит о том, что основным ее элементом является окно. Для успешного программирования разработчик должен иметь четкое представление о том, что подразумевается в Windows под этим понятием.

Для начинающих программистов неочевидно, что окнами Windows являются не только главные окна, но и большинство элементов управления в них, таких как поля ввода, списки, кнопки и т.п. Фактически любой элемент интерфейса, способный получать фокус ввода, является окном Windows. Окна могут иметь окно-владельца (Parent window). В этом случае остальные называются дочерними окнами (Child Window) и располагаются на поверхности владельца.

Поведение и внешний вид окна определяются его классом. Класс — это внутренняя структура Windows, описывающая шаблон, на основании которого операционная система создает окна. Перед созданием окна необходимо зарегистрировать его класс при помощи функции:

Функция получает структуру данных TWndClassEx, описанную как:

TWndClassEx = packed record

// размер структуры, должен быть равен
// SizeOf(TWndClassEx)
// адрес процедуры — обработчика сообщений // размер дополнительных данных класса // размер дополнительных данных окна // идентификатор модуля, в котором
// находится процедура обработки сообщений
// курсор, который будет появляться при
// прохождении указателя мыши над окном
// кисть, используемая при заполнении фона
// окна
// имя ресурса, содержащего меню окна

Рассмотрим ключевые элементы этой структуры подробнее:

Style Битовая маска, задающая стиль окна. Стиль — это набор флагов, указывающих операционной системе, какие действия надо предпринимать по умолчанию при возникновении тех или иных событий в окне. Стиль класса в основном определяет моменты, связанные с прорисовкой окна
lpfnWndProc Адрес процедуры — обработчика сообщений. Каждый раз, когда в Windows возникает какое-либо событие, относящееся к окну (например, появилась необходимость в перерисовке или переместился курсор мыши над окном), операционная система формирует сообщение, состоящее из идентификатора и двух целочисленных параметров — wParam и lParam, и вызывает эту функцию. Таким образом, функция обработки сообщений и определяет поведение всех окон, созданных с этим стилем
hIcon Идентификатор значка. Он будет выводиться в левом верхнем углу окна. Сам значок может быть загружен из ресурса функцией API LoadIcon
lpszMenuName Имя меню. Это меню должно быть оформлено в виде ресурса. Если имя не задано, окно по умолчанию не будет иметь меню
lpszClassName Имя класса. В дальнейшем на него можно будет ссылаться при создании новых окон

Как видно из этой структуры, именно на уровне класса окна определяется функция обработки сообщений, которая будет задавать поведение всех окон с этим классом.

После того как класс зарегистрирован, приложение может создавать окна этого класса функцией:

// идентификатор модуля, ассоциированного с
// окном
// дополнительный параметр, передаваемый в
// оконную процедуру с сообщением WM_CREATE
Читайте также:  Bluetooth для ноутбука dns windows 10

Функция CreateWindowEx позволяет задать конкретный вид окна и уточнить информацию, полученную от класса окна.

Сообщения — это базовый механизм информирования программ о событиях, на которые они должны реагировать. Ядром программы является зарегистрированная в классе окна функция обработки сообщений, вызываемая ядром Windows при появлении событий, на которые программа должна отреагировать. Получение сообщения окном означает вызов его оконной функции с параметрами, описывающими передаваемое сообщение. Например, сразу после своего создания окно получает сообщение WM_CREATE, при нажатии клавиш на клавиатуре — WM_KEYDOWN, WM_KEYUP, при перемещении мыши — WM_MOUSEMOVE и т.п. Без обработки сообщений окно не сможет даже отрисовать себя — рисование выполняется по получении сообщений WM_PAINT, WM_NCPAINT. В программе, написанной с использованием только Windows API, функция обработки сообщений обычно представляет собой оператор case. Альтернативами данного оператора являются различные сообщения, которые эта функция должна обработать.

TWinControl — оболочка окна Windows

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

Базовым классом, инкапсулирующим окно Windows, является TWinControl. Когда создается экземпляр наследника этого класса, VCL автоматически регистрирует соответствующий класс окна Windows и создает окно. Благодаря этому наследники TWinControl могут содержать в себе другие окна и обрабатывать сообщения Windows. Визуальные компоненты, не являющиеся наследниками TWinControl (такие как TLabel, TSpeedButton), не представляют собой окна в понимании Windows. Все их события эмулируются компонентом, в который они помещены.

Центральное свойство компонента TWinControl — Handle. Оно представляет собой идентификатор окна Windows, полученного при создании этого компонента. Указанный идентификатор можно использовать с любыми функциями Windows API, работающими с окнами. Например, нижеприведенный код прокручивает текст в TMemo на одну строку вниз:

procedure TForm1.Button1Click(Sender: TObject);

PostMessage(Memo1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);

TWinControl перед созданием окна вызывает виртуальный метод CreateParams, позволяя программисту задать низкоуровневые параметры создаваемого окна. В процедуру передается структура данных:

// Заголовок окна, соответствующий параметру
// lpWindowName
// Стиль окна, соответствующий параметру dwStyle // Расширенный стиль окна (dwExStyle) // Идентификатор окна-владельца (hWndParent) // Дополнительный параметр (lpParam) // Структура TWndClass, позволяющая задать
// параметры класса окна

Наследники TWinControl могут перекрыть CreateParams, создавая окна с требуемыми внешним видом и поведением. Например, необходимо создать форму, не имеющую заголовка, однако позволяющую изменять свои размеры. Delphi не предоставляет возможности задать такое поведение визуальными средствами, однако, перекрыв TForm.CreateParams, мы легко добиваемся нужного эффекта:

procedure TForm1.CreateParams(var Params: TCreateParams);

inherited; // Вызываем унаследованный обработчик, позволяя
// VCL подготовить «типовую» конфигурацию окна
with Params do
// И изменяем требуемые параметры
Style := Style and (not WS_CAPTION) or WS_THICKFRAME or WS_POPUP;

В качестве упражнения рекомендуем посмотреть на некоторые фрагменты реализации класса TWinControl, расположенного в модуле Controls.pas. Хорошо видно, что метод CreateWnd сначала вызывает метод CreateParams, заполняющий структуру WndClass с параметрами класса окна и параметры для CreateWindow, а затем регистрирует класс и создает окно. Также очень показателен метод TCustomForm.CreateParams, расположенный в модуле Forms.pas. Можно увидеть, как по свойствам Position, BorderIcons и FormStyle формируется набор флагов стиля окна для функции CreateWindow.

Каждое окно Windows должно обрабатывать сообщения. VCL берет на себя работу по организации цикла сообщений и по их базовой обработке. Для большинства сообщений Windows, которые должны обрабатываться окном, уже предусмотрена обработка по умолчанию. Сообщения, требующие специфической обработки, приводят к вызовам функций —обработчиков событий, например:

Читайте также:  Inspiron 15 3000 установка windows 10
WM_MOUSEMOVE OnMouseMove
WM_LBUTTONDOWN, WM_RBUTTONDOWN OnMouseDown
WM_LBUTTONUP, WM_RBUTTONUP OnMouseUp
WM_LBUTTONDBLCLK OnDblClick
WM_KEYDOWN OnKeyDown
WM_KEYUP OnKeyUp
WM_PAINT OnPaint

Показателен в этом смысле метод WndProc класса TWinControl или его наследников. При этом VCL перед вызовом обработчиков производит «упаковку» параметров сообщений в удобный для обработки и анализа вид. Понимание того, какое сообщение Windows вызывает срабатывание того или иного события VCL, очень помогает при программировании обработчиков и совершенно необходимо при написании собственных компонентов. Разумеется, предусматривать отдельные обработчики для каждого из сотен сообщений, которые могут поступить в окно, — значит неоправданно усложнять код VCL. Поэтому для обработки остальных сообщений синтаксис Object Pascal предусматривает создание процедур — обработчиков сообщений. Такие процедуры объявляются как:

procedure WMSize(var Message: TWMSize); message WM_SIZE;

В качестве параметра такая функция получает указатель на структуру TMessage, содержащую информацию о сообщении, переданном окну. Для многих часто используемых сообщений в модуле Messages.pas определены структуры, позволяющие более удобно работать с конкретными сообщениями.

Рассмотрим пример окна, обрабатывающего не предусмотренное VCL сообщение, в качестве которого используем WM_HOTKEY. Это сообщение посылается окну посредством зарегистрированной в Windows горячей клавиши, что позволяет программе реагировать на нее, даже не имея фокуса ввода:

procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);

// Объявляем процедуру-обработчик
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;

procedure TForm1.FormCreate(Sender: TObject);

// Регистрируем горячую клавишу Ctrl+Alt+F5
RegisterHotKey(Handle, 0, MOD_CONTROL or MOD_ALT, VK_F5);


procedure TForm1.WMHotKey(var Msg: TWMHotKey);

// Эта процедура вызывается при получении окном
// сообщения WM_HOTKEY
inherited; // Даем форме обработать сообщение,
// если у нее уже есть его обработчик
Beep; // Выполняем дополнительные действия


procedure TForm1.FormDestroy(Sender: TObject);
begin

// Отменяем регистрацию горячей клавиши
UnRegisterHotKey(Handle, 0);

Хочется обратить ваше внимание на вызов унаследованного обработчика в методе WMHotKey. Если вы не уверены, что хотите запретить обработку предком подобного сообщения, — всегда вызывайте унаследованный обработчик. В противном случае вы рискуете помешать VCL обрабатывать сообщения. Например, написав свой обработчик WM_PAINT и не вызвав в нем унаследованный, вы полностью заблокируете перерисовку формы средствами VCL.

Каждый наследник TWinControl может служить контейнером для других компонентов. Для связи между родительским и дочерними компонентами служат свойства Controls и Parent. Каждый визуальный компонент имеет свойство Parent, ссылающееся на оконный компонент, который является его владельцем. До тех пор пока не установлено это свойство, компонент не может обрабатывать сообщения Windows и отображаться на экране. Окно-предок ведет список вставленных в него компонентов, доступ к которому можно получить при помощи его свойства Controls.

TForm — просто окно

Delphi вводит новую для Windows концепцию — форма. Такого понятия нет в Windows, однако оно довольно часто встречается в высокоуровневых средствах разработки. Форма инкапсулирует окно верхнего уровня (top-level window), которое служит контейнером для остальных визуальных элементов программы. С точки зрения Windows — это такое же окно, как и все остальные, только с соответствующим образом подобранным набором стилей. В частности, на этом уровне реализованы поддержка главного меню, управление дочерними окнами (MDI). Как и любое окно, TForm имеет свойство Handle, которое может быть использовано в вызовах Windows API. Например, приведенный ниже код создает форму в виде пятиконечной звезды:

procedure TForm1.FormCreate(Sender: TObject);

Points: packed array [1..10] of TPoint =
((X:50; Y:0), (X:63; Y:33), (X:100; Y:33), (X:72; Y:55),
(X:90; Y:100), (X:50; Y:72), (X:10; Y:100), (X:28; Y:55),
(X:0; Y:33), (X:37; Y:33));

BorderStyle := bsNone;
Brush.Color := clRed;
SetWindowRgn(Handle, CreatePolygonRgn(Points, 10, WINDING), TRUE);

Концепция форм помогает разработчику в управлении окнами верхнего уровня, однако многие ее понятия (например, главное окно приложения, список окон (Screen.Forms)) не имеют прямых аналогов в Windows API.

Читайте также:  Windows update ru board

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

Базовым понятием графической подсистемы Windows является контекст графического устройства — внутренняя структура, определяющая набор графических объектов, их атрибутов и графических режимов, которые могут повлиять на вывод информации. Контекст может быть связан с различными устройствами, такими как дисплей, принтер, изображение в памяти, что позволяет использовать одни и те же функции для вывода информации на любое из этих устройств. Разумеется, при этом необходимо учитывать такие возможности конкретного устройства, как разрешение или поддерживаемый набор цветов.

Типичный сценарий вывода информации в Windows выглядит следующим образом:

Получить контекст устройства

Сформировать графические объекты с требуемыми атрибутами,
такие как шрифт, кисть
Включить объекты в контекст и, возможно, сохранить при этом старые
объекты
Установить атрибуты контекста (режим заполнения и т.п.), сохранив
старые атрибуты
Используя функции рисования, вывести информацию на устройство
Восстановить атрибуты контекста
Восстановить графические объекты
Освободить созданные временые графические объекты

Освободить контекст устройства

Хорошо видно, что собственно вывод информации занимает далеко не главную позицию в общем объеме кода. Большая часть времени уходит на создание, сохранение и восстановление графических объектов. При этом нередки трудноопределимые ошибки, приводящие к утечке ресурсов из-за неосвобожденных объектов.

VCL предлагает элегантное решение проблемы, описанной в предыдущем разделе, — класс TCanvas, инкапсулирующий контекст графического устройства Windows. Вместе с вспомогательными классами TFont, TBrush и т.д., реализующими графические объекты, этот класс берет на себя всю работу, связанную с получением и освобождением контекстов устройства его атрибутов и графических объектов. Когда вы меняете, например, свойства кисти (Canvas.Brush), TCanvas автоматически создаст новую кисть с нужными атрибутами и включит ее в свой контекст, а старую — корректно удалит. Наряду с этим TCanvas реализует ряд методов, позволяющих рисовать на нем без указания контекста устройства. В итоге программист может вообще не знать принципов работы графической подсистемы Windows, а использовать только методы TCanvas. Все классы VCL, допускающие рисование, имеют свойство Canvas, указывающее на автоматически создаваемый экземпляр TCanvas, связанный с ним.

Реализовать в TCanvas все функции рисования Windows нереально, поскольку это приведет к резкому усложнению кода VCL. Однако TCanvas имеет свойство Handle, которое представляет собой идентификатор графического контекста Windows, ассоциированного с экземпляром класса. Используя его, мы можем задействовать все многообразие функций API, предназначенных для рисования. При этом всю работу по установке атрибутов и недопущению утечки ресурсов можно выполнить в реализации класса TCanvas.

Рассмотрим наиболее часто употребляемые функции Windows API, которые можно использовать для рисования.

Оцените статью