ActiveX в Delphi

В пятую версию Delphi вошли

В пятую версию Delphi вошли следующие новые технологии и возможности:
- ADO (ActiveX Data Object). Является альтернативной технологией по отношению к BDE (Borland Database Engine), позволяющей получать доступ к данным различного формата;
- средство проектирования Data Module Designer (Редактор модулей данных). Позволяет создавать и работать с модулями данных;
- компоненты подсистемы InterBase Express. Позволяют пользоваться серверами InterBase удобным способом. При этом использование BDE не требуется;
- расширение MIDAS. В новой версии Delphi технология MIDAS поддерживает удаленные модули данных;
- изменения в CORBA. CORBA теперь может работать с VisiBroker для C++ ORB версии 3.32. Существенно уменьшено количество сообщений между клиентом CORBA и сервером;
- новые возможности отладки программ. Встроенный отладчик имеет новые характеристики, включая способность устанавливать отладку параметров для специфических процессов;
- расширения VCL (Visual Component Library). VCL теперь содержит несколько новых объектов, свойств и событий, в том числе новые компоненты для работы с Web и связью;
- фреймы (Frames). Это специальный тип форм, которые могут размещаться в пределах других форм или фреймов;
- возможность настройки рабочего стола и сохранение нескольких вариантов настроек;
- список To-Do. Содержит список задач, которые необходимо выполнить для завершения проекта;
- категории свойств в инспекторе объектов. Инспектор объектов позволяет фильтровать свойства и события по категориям;
- изображения на выпадающих списках инспектора объектов. В инспектор объектов добавлена возможность просмотра в выпадающих списках небольших рисунков, таких как изображения курсоров;
- новые возможности менеджера проектов (Project Manager). Менеджер проектов упрощает управление проектами. Теперь можно свободно оперировать файлами различных проектов, перегруппировывать их, добавлять любой файл к проекту. Файлы ресурсов, которые добавляются к проекту, компилируются в файлы с расширением RES и присоединяются к проекту;
- расширение возможностей ActiveX. Импортируемые серверы СОМ могут быть использованы как компоненты для визуальной разработки;
- новые мастера (Wizards). Два новых мастера помогают создавать приложения для Панели управления Windows и консольных приложений;
- улучшения редактирования. Предпочтения по редактированию теперь централизованы и вызываются отдельной командой Tools/Editor Options (Средства/Настройки редактора);
- новое окно просмотра проекта
- сохранение файлов форм в виде текста. Файлы форм (DFM) теперь по умолчанию сохраняются в виде текста, а не в двоичном виде, как раньше. Имеется возможность сохранения форм и в бинарном файле;
- новые возможности автоматического создания форм. В прежних версиях Delphi, при создании новых форм, они все, за редким исключением, помещались в список автоматически создаваемых форм (Autocreated Forms). В новой версии Delphi разработчик может самостоятельно выбрать, помещать ли вновь создаваемые формы в этот список, либо в список доступных форм (Available Forms), в данном случае в список автоматически создаваемых форм поместится только главная форма проекта;
- параметры в командной строке IDE. В новой версии Delphi можно запускать IDE из командной строки с использованием нескольких новых параметров, особенно полезных для отладки. Например, параметры, позволяющие отображать, сколько оперативной памяти занято в настоящий момент, выводить все сообщения в log-файл и др.;
- международные инструментальные средства. Новый блок инструментальных средств, называемый встроенной средой перевода (Integrated Translation Environment), предназначен для упрощения программной локализацией и одновременной разработки разноязычных версий;
- компоненты NetMaster. Компоненты NetMaster размещаются на отдельной вкладке палитры компонентов, которая называется FastNet.
Все вышеперечисленное вошло в версию Delphi Enterprise, в других версиях какие-то из описанных нововведений могут отсутствовать. Рассмотрим некоторые из перечисленных нововведений более подробно.
Новый редактор модулей данных (Data Module Designer) представляет собой одно из средств визуализации разработки приложений. Модуль данных выглядит как полноценное представление модели данных, используемой в разрабатываемом приложении (рис. 1.1). Редактор модулей данных можно открыть с помощью команды главного меню File/New/Data Module (Файл/Новый/Модуль данных).
Если посмотреть на страницу диаграммы данных (Data Diagram), то можно увидеть диаграмму, внешне напоминающую диаграмму "сущность-связь", которая обычно изображается при проектировании данных с помощью CASE-средств. Редактор модулей данных предоставляет разработчику простое визуальное средство проектирования баз данных. В частности, при соединении изображений компонентов-наследников TDataSet с помощью мыши будут автоматически добавлены новые необходимые компоненты (например, TDataSource), будут установлены значения свойств для обеспечения этой связи. Тип устанавливаемой связи можно указать, нажав соответствующую кнопку на вертикально расположенной панели инструментов, находящейся слева от диаграммы. Кроме компонентов TDataset, на странице диаграммы данных могут быть отображены и другие компоненты, с которыми можно работать таким же образом.
Начиная с пятой версии, Delphi поддерживает так называемый список То-Do - список действий, которые необходимо выполнить для завершения текущего проекта.

В пятую версию Delphi вошли

Рис. 1.1. Редактор модулей данных
Задания списка To-Do могут относиться не только целиком к проекту, но и к отдельному модулю. Список действий можно получить, выбрав в главном меню Delphi команду View/To-Do List (Просмотр/Список действий). В появившемся окне (рис. 1.2) можно добавлять задания в список, отмечать выполненные, редактировать их и менять приоритет выполнения заданий.

В пятую версию Delphi вошли

Рис. 1.2. Список То-Do
Добавление задания в список можно осуществить с помощью контекстного меню, либо с помощью комбинации клавиш ++. При этом появляется окно создания нового задания (рис. 1.3).
Задания, внесенные в список To-Do для отдельного модуля, хранятся в качестве комментариев в исходном тексте модуля:
{ TODO 1 : Изменить название кнопке ОК }

В пятую версию Delphi вошли

Рис. 1.3. Создание нового задания
Задания для всего проекта хранятся в файле с расширением TODO. В списке To-Do задания для отдельного модуля и всего проекта отображаются разными значками. Если модуль, к которому относится задание в списке, не загружен в редактор кода, то данное задание будет выделено серым цветом. Каждое задание может иметь приоритет, имя исполнителя, категорию, текст задания и отметку о выполнении:
{ DONE 1 : Изменить название кнопке ОК }
По всем из вышеперечисленных параметров список To-Do может быть отсортирован.
Новая версия Delphi позволяет создавать многоязыковые приложения. Причем позволяет это делать параллельно. Например, когда создается одновременно русская и английская версия приложения. Рассмотрим, как, используя новые средства Delphi, под общим названием встроенная среды перевода (Integrated Translation Environment), можно создать одновременно русскую и английскую версию приложения. Для этого создадим новый проект, который состоит из одной формы, на которой расположим две кнопки. Затем в главном меню Delphi выберем команду Project/Languages/Add (Проект/Языки/Добавить), после чего запустится мастер (рис. 1.4), выдающий последовательность диалоговых окон, в которых нужно выбрать поддерживаемые приложением языки, в нашем случае - это русский и английский (США).
После выполнения всего вышеописанного, будет автоматически запущен менеджер перевода (Translation Manager) (рис. 1.5).
Менеджер перевода представляет собой редактор строковых ресурсов, которые могут присутствовать в форме. В менеджере перевода имеются сведения о тех свойствах элементов интерфейса, которые могут отличаться в разноязычных версиях. В число этих свойств входят не только такие, как caption или text, но и другие визуальные характеристики. Изменим для каждой версии названия кнопок (Buttoni. Caption и Button2. Caption): в английской версии на "Yes" и "No" и "Да", "Нет" для русской версии. Сменим в русской версии название формы (Formi. caption) на "Приложение". Теперь дадим через главное меню Delphi команду Project/Language/Set Active (Проект/Язык/ Сделать активным) и выберем русский или английский язык. Откомпилируем проект, и мы получим нужную языковую версию (рис. 1.6).

В пятую версию Delphi вошли

Рис. 1.4. Мастер создания многоязычных приложений Add Languages

В пятую версию Delphi вошли

Рис. 1.5. Окно Translation Manager

В пятую версию Delphi вошли

Рис. 1.6. Версии разноязычных приложений
В библиотеку визуальных компонентов (VCL) Delphi также были внесены значительные изменения. Среди них:
- компонент TApplicationEvents. В ранних версиях Delphi для создания обработчика событий объекта TAppiication необходимо было сначала создать процедуру, а затем присвоить имя этой процедуры обработчику события TAppiication. В пятой версии Delphi появился новый невизуальный компонент. Можно поместить его на форму приложения и в окне инспектора объектов (описание инспектора объектов см. далее в этой главе) станет возможным обрабатывать все события TAppiication, причем работать привычным образом, так же, как и с другими компонентами Delphi;
- класс TAppietAppiication. Специально для создания приложений для панели управления Windows, имеющих расширение CPL, в Delphi 5 был введен новый класс TAppietAppiication. Приложение подобного типа может иметь в своем составе несколько модулей - потомков класса TAppletModuie, каждый из которых будет отображен в панели управления Windows. Для создания такого приложения необходимо выбрать пункт главного меню Delphi File/New/Control Panel Application (Module) (Файл/Новый/Приложение панели управления);
- новые события компонентов TTooiBar и TListview. Для этих компонентов был добавлен ряд событий (onAdvancedCustomDrawItem, OnAdvancedCustomDraw,
и др.). Обработчики этих событий имеют дополнительный параметр, указывающий, на какой стадии перерисовки изображения наступает данное событие;
- введены новые классы контейнерного типа. Это классы-потомки класса TList: TObjectList, TComponentList и TciassList. Данные классы предна-значены для хранения ссылок на объекты, компоненты и классы соответственно. Также в Delphi 5 введены новые классы TObjectQuery и TObjectstack, которые предназначены для хранения ссылок на объекты в виде очередей и стеков.
Одним из очень полезных нововведений в Delphi 5 стали фреймы (frames). Фреймы - это визуальные контейнеры, которые можно создавать, размещать на них различные компоненты (так же, как и на формах) и, в дальнейшем, использовать их в формах. Создать новый фрейм можно с помощью пункта главного меню Delphi File/New Frame (Файл/Новый фрейм). После того как все необходимые фреймы будут созданы, их можно размещать на формах вашего приложения с помощью компонента Frames в стандартной палитре компонентов. Важным фактором является то, что при размещении фрейма на форме Delphi создает наследника класса выбранного фрейма. То есть, при изменении свойств компонентов в исходном фрейме эти изменения автоматически происходят в фреймах-наследниках. При желании, можно зарегистрировать часто используемый фрейм в палитре компонентов.
Для этого достаточно выполнить стандартную процедуру регистрации компонента.
Среда программирования Delphi изначально была ориентирована на работу с базами данных. Поэтому от версии к версии происходили улучшения старых и создание новых механизмов работы с базами данных. В Delphi 5 расширился набор компонентов для работы с базами данных. В палитре компонентов появилась новая вкладка InterBase Express. На ней расположены компоненты, предназначенные для доступа к серверу баз данных IB Database версии 5.5 или выше. При этом все компоненты данной вкладки используют его клиентский API, что полностью избавляет от необходимости использовать BDE.
Еще одно новое средство для работы с базами данных - это ADO (ActiveX Data Objects). Компонентам ADO выделена одноименная вкладка палитры компонентов. ADO - представляют собой СОМ-серверы, использующие OLE DB для работы с данными. СОМ-серверы входят в состав многих продуктов фирмы Microsoft. ADO-компоненты для своей работы также не требуют наличия BDE. В Delphi 5, в связи с поддержкой ADO, введены новые классы-потомки TFields, разработанные для поддержки типов данных, которые характерны для ADO.
Естественно, не обошлось без изменений компонентов, поддерживающих BDE. Начиная с пятой версии Delphi, у компонента TDatabase появился новый метод Execute, который позволяет выполнять SQL-запросы без использования специального компонента TQuery.
Ну и, конечно, не осталась без внимания разработчиков Delphi такая технология, как COM. Delphi 5, при импорте библиотек типов в приложениях, позволяет установить СОМ-серверы в палитру компонентов. Полученные в результате компоненты являются наследниками класса TOLEServer и обладают событиями, которые имеются у данного объекта. Это позволяет создавать обработчики событий СОМ-серверов с помощью инспектора объектов. Свойства СОМ-серверов доступны только на этапе выполнения приложения, поэтому в инспекторе объектов они не отображаются. Поддержку СОМ-серверов мы подробно рассмотрим в главе 3.

Генератор исходного кода. Создание простейшего приложения

При работе с компонентами в конструкторе форм Delphi автоматически генерирует соответствующий код на языке Object Pascal. Самый простой метод знакомства с автоматической генерацией кода - начать новый проект, для
этого в главном меню Delphi нужно выбрать команду File/New/Project (Файл/Новый/Проект). После чего, в конструкторе форм появится новая форма, а в редакторе кода можно видеть уже сгенерированный текст - заготовку будущей программы на языке Object Pascal (листинг 1.1).

Листинг 1.1
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type
TForml = class(TForm)
private
{ Private declarations }
public
( Public declarations }
end; var
Forml: TForml;
implementation
{$R *.DFM}
end.

Рассмотрим листинг 1.1 более подробно. На нем представлена "заготовка" модуля. В данном случае его название - Unit1. Модуль содержит элементы, которые можно использовать в программе или в других модулях. К числу стандартных модулей среды Delphi относятся такие модули, как system, Sysutiis и др. После компиляции модуля Delphi сохраняет результаты в файле модуля с расширением DCU (Delphi Compiled Unit).
Стандартно, модуль состоит из заголовка и четырех разделов (листинг 1.2).

Листинг 1.2
unit название;
interface
{раздел интерфейса}
uses
{список используемых модулей};
const
{список констант};
type
{описание типов};
var
{объявление переменных};
{заголовки процедур, имеющихся в модуле};
{заголовки функций, имеющихся в модуле};
implementation
{раздел реализации}
uses
{список используемых модулей};
const
{список констант};
type
{описание типов};
var
{объявление переменных};
{описание процедур};
{описание функций};
{директивы препроцессора};
initialization
{раздел инициализации}
{операторы, команды};
finalization
{раздел завершения}
{операторы, команды};
end.

Первый раздел - раздел описания (interface), является как бы "лицом" данного модуля. В нем размещены описания модулей, которые использует модуль, а также в нем объявляются типы, константы, переменные, функции и процедуры, описываются идентификаторы, которые являются доступными всем модулям и программам, использующим данный модуль. Заметим, что для функций и процедур в этом разделе записываются только их заголовки. Непосредственно процедуры и функции размещены в следующем разделе - разделе реализации.
Во втором разделе - разделе реализации (implementation), кроме процедур и функций описываются типы, константы, переменные, а также модули, которые используются только в этом модуле, а за его пределами не видны. Кроме того, в данном разделе размещаются директивы препроцессора - служебные команды среды разработки, например:
{$R *.DFM}.
Директивы препроцессора - это указание компилятору на выполнение каких-либо действий. Их записывают в форме {$ директива}. Директивы препроцессора делятся на глобальные и локальные. Глобальные директивы действуют на весь компилируемый файл, локальные - от места их объявления до появления отменяющей директивы.
Имеются три вида директив: ключевые директивы, директивы параметров и директивы условной компиляции. Первые два вида директив обладают установками по умолчанию. Для установки этих директив вы можете воспользоваться вкладкой Compiler (Компилятор) диалогового окна Project Options (Настройки проекта), вызываемого из главного меню Delphi Project/Options (Проект/Настройки). Для того чтобы посмотреть, какие директивы препроцессора установлены по умолчанию, достаточно, находясь в окне редактора кода, нажать комбинацию клавиш +, а затем - <О>. В результате, в начале модуля вставятся все значения директив препроцессора, которые используются компилятором в настоящий момент (листинг 1.3).

Листинг 1.3
{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,
N+,0+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE$00100000}
{$IMAGEBASE $00400000}
($APPTYPE GUI)

Перечислим основные типы директив препроцессора:
- директивы условной компиляции;
- директивы проверки утверждений;
- директивы контроля ввода/вывода;
- директивы, определяющие размер стека;
- директивы информации о типах;
- директивы проверки переполнения при целочисленных операциях;
- директива связывания ресурсов;
- директивы проверки диапазона;
- директива создания консольного приложения.
Директивы условной компиляции позволяют вам управлять компиляцией программного кода. Схема задания условной компиляции может быть двух видов:
{$If условие}
{компилируемый код};
{$EndIf}
И
{$If условие}
{компилируемый код};
{$Else}
{компилируемый код};
{$EndIf}
В обоих случаях, при выполнении некоторого условия, записанного в строке $If, код, расположенный после $If, - компилируется. Иначе, в первом случае - он игнорируется и в результирующий файл не попадает, во втором - выполняется код, записанный после директивы $Else.
Рассмотрим теперь сами условные директивы. Первая директива:
{$IfDef условный идентификатор}
Данная директива проверяет, был ли определен указанный в ней условный идентификатор. Если он был определен, то следующий за ней код будет откомпилирован. Если же директивы, определяющей условный идентификатор, не было, либо она была отменена, то следующий за директивой код откомпилирован не будет.
Для определения условного идентификатора используется директива:
{$Define условный идентификатор}
Для отмены определения условного идентификатора служит директива:
{$UnDef условный идентификатор}
Кроме директивы $IfDef имеется также обратная ей директива $IfNDef.
Она возвращает истинное значение, если указанный в ней условный идентификатор не определен.
Данный вид директив полезен при отладке приложений. Например, программисту часто приходится вводить некоторые промежуточные переменные и объекты, которые необходимы для отладки и не нужны в окончательной версии приложения. Для этого удобно ввести конструкцию вида:
($IfDef Debug}
операторы отладки {$EndIf}
Если в начале программы вы введете директиву
{$Define Debug}
операторы отладки будут компилироваться и выполняться. Но когда вы либо уберете эту директиву, либо закомментируете ее:
//{$Define Debug}
либо введете после нее директиву
{$Undef Debug}
все операторы отладки будут убраны из программы.
Директивы проверки утверждений служат для разрешения или запрещения проверки утверждения. Они влияют на работу процедуры Assert, которая используется при отладке программ. Процедура Assert применяется при от
ладке программ для проверки истинности утверждений, которые по замыслу должны быть истинны, но в силу каких-то ошибок могут нарушаться. Процедура приводит к прекращению работы, генерации исключения EAssertionFaiied и выдаче сообщения об ошибке в случае, если проверяемое утверждение окажется ложным.

Примечание
Информацию об исключительных ситуациях и обработке исключений смотрите во второй главе книги.

Директивы проверки утверждений являются локальными и имеют следующий вид:
{$С+} или ($ASSERTIONS ON}
и
{$С-} или ($ASSERTIONS OFF}
Данная директива действует на весь файл независимо от места своего расположения в нем.
Директивы контроля ввода/вывода служат для включения и выключения контроля работы с файлами. Синтаксис данных директив следующий:
{$I+} или {$IOCHECKS ON}
и
{$I-} или {$IOCHECKS OFF}
При включенной проверке ввода/вывода в файл (директива {$I+} или ($IOCHECKS ON}) в случае возникновения ошибки ввода/вывода генерируется исключение EinOutError. Если действует директива {$I-} или {$IOCHECKS OFF}, то исключение не генерируется.
Директивы, определяющие размер стека, необходимы для задания минимального и максимального размера оперативной памяти, выделяемой под стек. Все локальные переменные процедур и функций размещаются в стеке приложения. При вызове процедуры или функции ее локальные переменные помещаются в стек. При выходе из процедуры или функции локальные переменные удаляются из стека. Директивы препроцессора
{$М минимальный размер стека, максимальный размер стека}
или
($MINSTACKSIZE минимальный размер стека} ($MAXSTACKSIZE максимальный размер стека}
указывают минимальный и максимальный размер стека. Windows гарантированно выделяет приложению область памяти, необходимую для мини
мального размера стека. В случае, если памяти недостаточно, Windows выдаст ошибку при попытке запуска приложения.
Значение минимального размера стека должно находиться в пределах между целыми числами 1024 и 2147483647. Максимальное значение стека должно быть целым числом, расположенным между минимальным размером стека и 2147483647.
В случае нехватки памяти под стек во время работы приложения, размер стека увеличивается на четыре килобайта, при условии, что в данном случае размер стека не превысит максимальный размер. Если размер стека превышает допустимый, генерируется исключение EStackOverflow.
Директивы информации о типах включают или выключают генерацию информации о типах во время выполнения (RunTime Type Information, RTTI). Синтаксис директив:
{$М+} или {$TYPEINFO ON}
и
{$М-} или {$ TYPEINFO OFF}
Директивы проверки переполнения при целочисленных операциях включают или выключают проверку переполнения при выполнении операций над целыми числами (+, -, *, Abs, sqr, Succ, Pred, Inc и Dec). Синтаксис директив:
{$Q+) или {$OVERFLOWCHECKS ON}
и
{$Q-} или {$OVERFLOWCHECKS OFF}
При включенной директиве {$Q+} осуществляется проверка результата вычисления на переполнение. Если переполнение обнаружено, то генерируется исключение EIntOverfiow. Эти директивы проверяют только результаты арифметических операций. Данная директива по умолчанию отключена, т. к. значительно увеличивает размер приложения и замедляет его работу. Целесообразно использовать директиву проверки переполнения при целочисленных операциях на этапе отладки приложения.

Примечание
При отключенной директиве могут появиться ошибки в расчетах, причем сообщений об ошибках не будет.

Директива связывания ресурсов предназначена для связывания файлов ресурсов с исполняемым модулем. Синтаксис директивы:
{$R ресурсы} или {$RESOURCE ресурсы}, где ресурсы - имя или шаблон файла.
Данная директива указывает файлы ресурсов, имеющие расширение DFM или RES, которые включаются в выполняемый модуль. Заметим, что при генерации заготовки модуля формы (листинг 1.1) Delphi самостоятельно прописывает строку {$R имя-dfm}, которая обеспечивает компоновку файла ресурсов формы.

Примечание
Не удаляйте и не изменяйте строку {$R имя.dfm}, т. к., в данном случае, Delphi не сможет найти файл ресурсов и сгенерируется исключение EResNotFound.

Директивы проверки диапазона предназначены для включения и выключения проверки диапазона целочисленных значений и индексов. Директивы имеют следующий синтаксис:
{$R+} или {$RANGECHECKS ON)
и
{$R-} или {$RANGECHECKS OFF}
Данные директивы, соответственно, включают и выключают проверку диапазона целочисленных значений и индексов. Если включена директива {$R+}, то все индексы массивов и строк и все присваивания скалярным переменным и переменным с ограниченным диапазоном значений проверяются на соответствие значения допустимому диапазону. Если требования диапазона нарушены или присваиваемое значение слишком велико, генерируется исключение ERangeError. Так же, как и директива проверки переполнения, директива {$R+} замедляет работу приложения и увеличивает его размер. Обычно она используется во время отладки приложения.
Директива создания консольного приложения предназначена для указания компилятору того факта, что данное приложение является приложением для DOS. При этом необходимо удалить из проекта все формы и визуальные объекты. Синтаксис директивы:
($APPTYPE CONSOLE}

Примечание
Консольное приложение можно создать и другим способом. Для этого необходимо выполнить команду меню главного окна Delphi Project/Options (Проект/Настройки) и на вкладке Linker (Установка связей) установить параметр Generate Console Application (Создать консольное приложение).

Последние два раздела не являются обязательными. В разделе инициализации (initialization) размещаются операторы и команды, которые должны выполняться в начале работы программы, которая подключает эти модули. При наличии раздела инициализации в модуле может присутствовать раздел
завершения (finalization). В данном разделе находятся операторы и команды, которые выполняются при завершении работы программы.

Интегрированная среда разработки Delphi (IDE)

Для программиста очень важным, если не главным, является удобство работы со средой разработки. От этого зависит как качество, так и скорость создания приложения. Интегрированная среда разработки Delphi достаточно удобна для эффективного и быстрого создания приложений. Авторы Delphi длительное время совершенствовали среду разработки. Не менее важным является возможность настройки среды программирования по собствен
ному желанию. Delphi позволяет разработчику настраивать среду "под себя". В этом разделе мы рассмотрим основные элементы интегрированной среды Delphi 5 (6).

Объектно-ориентированное программирование

Delphi в качестве внутреннего языка программирования использует Object Pascal. Главное отличие Object Pascal от языка программирования Pascal в том, что Object Pascal работает с объектами. Библиотека визуальных компонентов (VCL) представляет из себя иерархию классов, написанных на языке Object Pascal и объединенных в интегрированной среде разработки (IDE) Delphi. Разработчики могут создавать собственные объекты и классы и обмениваться ими. Это дает возможность достаточно быстро создавать приложения. Используя палитру компонентов и инспектор объектов, разработчик может размещать на формах компоненты и изменять их свойства без написания какого-либо кода.
Итак, что же такое объект? Класс - это тип данных, который включает в себя данные и операции над данными, а объект - это экземпляр какого-либо класса. Таким образом класс - это описание (тип), а объект - это то, что создано в соответствии с этим описанием. До создания объектно-ориентированных языков программирования данные и операции над данными рассматривали как отдельные элементы. Что такое объект, достаточно просто можно понять, если раньше вам приходилось работать с записями в Pascal. Записи состоят из областей, которые содержат данные разных типов (каждая область записи имеет собственный тип данных). Записи были созданы для облегчения работы с элементами, которые содержат различные типы данных.
Объекты - это тоже хранилища разных типов данных. Данные объекта называются полем (field) и аналогичны полям записи. Но объекты, в отличие от записей, содержат еще процедуры и функции, которые применимы к полям данного объекта. Эти процедуры и функции называются методами (methods). Изменять поля объекта также можно через свойства (properties) объекта. Каждое свойство объекта в Delphi представляет собой поле и методы, которые позволяют считывать значение поля и задавать его (так называемые методы доступа). Свойства можно изменять в процессе разработки приложения без написания какого-либо кода с помощью инспектора объектов. Если доступ к полям объекта извне осуществляется только посредством методов доступа, такое свойство объекта называется инкапсуляцией. В дополнение к инкапсуляции, объектно-ориентированное программирование характеризуется еще свойствами наследования и полиморфизма. Наследование обозначает, что объекты могут получать свои свойства и методы от других объектов (которых называют в данном случае предками). Объекты-наслед
ники берут от предков все свойства, методы и поля. Эти свойства, методы и поля в объекте-наследнике могут сохраняться в неизменном виде, либо могут быть изменены. Кроме того, объекты-наследники имеют в своем составе дополнительно новые поля, методы или свойства. Полиморфизм подразумевает, что всегда вместо метода предка может быть подставлен метод объекта-потомка и будет вызван метод фактически переданного объекта (при этом, естественно, методы потомка и предка могут иметь совершенно разное содержание).
Объекты в Delphi динамически занимают блоки памяти, структуру которых определяет их класс. Каждый объект является уникальным экземпляром своего класса и имеет все методы класса.
Типичное определение нового класса выглядит следующим образом:
type Имя класса = class (Класс-предок)
{Список состава класса} private
{частные описания} protected
{защищенные описания} public -
(общедоступные описания} published
{опубликованные описания} end;
где имя класса - любое корректное имя (выбирается произвольно), Класс-предок - название класса, наследником которого является создаваемый класс, Список состава класса - содержит свойства и методы нового класса. Для каждого элемента класса можно установить разную видимость. Для этого предназначены четыре ключевых слова: private, protected, public и published. Видимость элемента класса определяет, где в программе и как будет виден данный элемент класса. Минимальная видимость элемента класса задается ключевым словом private. Ключевое слово protected задает средний уровень видимости, a public и published - наивысшую степень доступности. Например, строка
published property Color: TColor read GetColor write SetColor;
определяет свойство color для некоего класса, которое является опубликованным (published), т. е. имеет наивысшую видимость.
Если перед описанием элемента класса не ставится ключевое слово, определяющее его степень видимости, то считается, что видимость элемента такая же, как и у предыдущего элемента класса, например:
published property Color: TColor read GetColor write SetColor; property BackColor: TColor read GetBackColor write SetBackColor;
В данном примере оба свойства - Color и BackCoior - являются опубликованными.
Рассмотрим все четыре ключевых слова более подробно.
- Private (частные) - определяет элементы класса, которые не видны вне модуля, в котором был объявлен класс, содержащий эти элементы. Другими словами, частные методы не могут быть вызваны из других модулей, а частные поля или свойства не могут быть считаны или изменены из других модулей.
- Protected (защищенные) - определяет элементы класса, которые видны только внутри модуля, где определен класс, содержащий эти элементы, а также внутри других модулей, где присутствуют классы-потомки данного класса.
- Public (общедоступные) - определяет элементы класса, которые видны в любом месте программы и из любых модулей, в которых виден сам класс.
- Published (опубликованные) - определяет элементы класса, имеющие ту же видимость, что и public-элементы. Единственное отличие заключается в том, что опубликованные элементы порождают информацию о типе времени выполнения (RTTI). Благодаря данной информации, Delphi может осуществить проверку принадлежности элементов объекта тому или иному классу. Delphi использует RTTI для доступа к значениям свойств при сохранении и загрузке файлов форм (*.DFM), чтобы иметь возможность отобразить свойства в инспекторе объектов и ассоциировать конкретные методы с конкретными свойствами. Все методы классов могут быть опубликованы, за исключением перегруженных (overload) методов, имеющих одинаковые имена.
Кроме свойств и методов, определение нового класса может содержать список интерфейсов (interfaces), поддерживаемых данным классом (подробно об интерфейсах см. вторую главу книги).
В Delphi имеется понятие абстрактного класса. Абстрактный класс описывает несуществующий объект. Он нужен для того, чтобы описать некоторые фундаментальные свойства и методы объектов, которые создаются из абстрактного класса. Все объекты в Delphi созданы из абстрактного класса TObject. Класс TObject - предок многих простых классов. Он объединяет в себе основные функции, которые свойственны всем объектам Delphi. Класс TObject обеспечивает:
- возможность создания, уничтожения и управления экземплярами объектов, а также резервирование памяти и ее освобождение после уничтожения экземпляра;
- поддержку информации об объектах и типах; - поддержку обработки сообщений.
Итак, все классы в Delphi - потомки класса TObject. Класс называется прямым потомком класса TObject, если он произведен непосредственно от класса TObject. Класс называется косвенным потомком от класса TObject, если он произведен от класса, являющегося прямым или косвенным потомком класса TObject (т. е. произведен от промежуточного класса).

Примечание
В другой литературе можно встретить названия дочерний класс и класс-потомок. Дочерний класс - это прямой потомок, а класс-потомок - это косвенный потомок в терминологии данной книги.

Если при создании нового класса (о создании классов читайте во второй части книги) не указывается родительский класс, то считается, что родитель - класс TObject.
Все компоненты в библиотеке визуальных компонентов созданы из абстрактного класса TComponent. Компоненты являются объектами, свойства которых разработчик может изменять во время создания приложения. Класс TComponent включает в себя наиболее общие свойства и методы компонентов, такие как:
- возможность включать компонент в палитру компонентов и работать с ним во время разработки приложения;
- способность быть владельцем других компонентов или быть управляемым другими компонентами;
- возможность обмена данными с файлами и потоками;
П возможность служить оболочкой элементов ActiveX и других объектов.
Визуальные компоненты (которые отображаются на экране монитора во время работы приложения), такие как TForm и TSpeedButton, называются элементами управления и их предком является абстрактный компонент
TControl.
На рис. 1.19 представлена упрощенная диаграмма иерархии визуальных компонентов. На этой диаграмме изображены несколько компонентов, являющихся элементами управления и имеющих в качестве предка компонент
TControl.
Несмотря на свое название, библиотека визуальных компонентов состоит по большей части из невизуальных объектов. Во время создания приложения разработчик может размещать на формах невизуальные компоненты (они не будут отображаться во время запуска приложения) и изменять их свойства в точности так же, как при работе с визуальными объектами.
Кроме вышеописанных классов, в число базовых классов в Delphi входят
TWinControl, TCustomEdit И TBaseArray.

Объектно-ориентированное программирование

Рис. 1.19. Упрощенная диаграмма иерархии библиотеки визуальных компонентов (VCL) Delphi
К основным понятиям объектной модели Delphi относятся так называемые области видимости объектов. Область видимости объекта определяет ту часть кода Object Pascal, в которой доступны его (объекта) свойства и методы без явного указания объекта. Когда вы пишите код, который находится в области видимости объекта, вам необязательно указывать имя этого объекта для доступа к его свойствам и методам. Например, когда вы помещаете новую кнопку на форму, то вы можете обращаться к свойствам и методам формы в обработчиках событий кнопки без указания имени формы (листинг 1.4).

Листинг 1.4
procedure TForml.ButtonlClick(Sender: TObject); begin
Color := clGray;
Buttonl.Color := clTeal; end;

Примечание
В листинге 1.4 строка, задающая цвет кнопки Buttonl, может не работать в некоторых версиях Delphi. Автор проверял работоспособность данного кода в Delphi 5 версии Enterprise.

На приведенном выше в листинге 1.4 обработчике события кнопки Onclick строка
Color := clGray;
равносильна строке
Forml.Color := clGray;
Вам не нужно указывать название формы, т. к. метод Button1Click является частью объекта TForm1.
Вторая строка листинга 1.4 изменяет свойство объекта Buttonl, а не формы, поэтому нуждается в указании имени объекта.
Delphi создает отдельный файл модуля для каждой формы. Поэтому, для того чтобы иметь доступ из одного модуля к объекту, расположенному в другом модуле, вам потребуется указывать полное имя объекта, например:
Form2.Buttonl.Color := clTeal;
Точно так же можно вызывать на исполнение методы объектов, расположенных на другой форме (в другом модуле), например:
Form2.Editl.Clear;
Кроме всего вышеописанного, для использования объектов модуля Form2
(unit2) в модуле формы 1 (unit1) вам потребуется прописать в разделе uses
модуля uniti модуль Unit2:
implementation
uses Unit2;

Основные части интегрированной среды разработки

Интегрированная среда разработки Delphi состоит из четырех основных частей (окон), которые отображаются при запуске среды:
- главного окна (Main Window); - конструктора форм (Form Designer); - инспектора объектов (Object Inspector); - редактора кода (Code Editor).
Главное окно является основной частью интегрированной среды разработки (рис. 1.7). Оно выглядит как стандартное окно Windows и содержит в себе три составляющих его части: меню, панели инструментов и палитру компонентов.

Основные части интегрированной среды разработки

Рис. 1.7. Главное окно Delphi
Меню главного окна Delphi содержит стандартные для любой программы пункты. Меню - это самая верхняя строка главного окна Delphi. При помощи меню можно работать с файлами, проектами, формами, настраивать установки Delphi и многое другое. Меню главного окна позволяет устанавливать новые компоненты, а также вызывать другие инструменты разработки. Некоторые пункты меню Delphi можно выполнить при помощи "горячих клавиш", например, команду Open Project (Открыть проект) можно выполнить с помощью комбинации клавиш +.
Панели инструментов состоят из шести панелей, расположенных под меню главного окна (панель Desktop расположена справа от меню) и содержат кнопки для вызова некоторых команд меню главного окна. Панели разбиты по функциональному назначению.
Стандартная панель (Standard) содержит кнопки для работы с файлами проекта и позволяет создавать, сохранять и удалять файлы проекта.
Панель просмотра (View) служит для работы с формами и текстом программ. Панель отладки (Debug) позволяет запускать и отлаживать приложение. Панель пользователя (Custom) предназначена для разработчика.
Панель рабочий стол (Desktop) - позволяет сохранять сведения о том, какие окна среды разработки открыты и где именно они расположены на экране. Такие сведения называются конфигурацией рабочего стола Delphi. Delphi позволяет создавать и сохранять несколько конфигураций рабочего стола. Кроме того, можно выделить конфигурацию, которая будет автоматически загружаться в режиме отладки (debug desktop) и выгружаться при выходе из пего. Для вышеперечисленных целей можно использовать кнопки: Save current desktop (Сохранить текущую конфигурацию рабочего стола) и Set debug desktop (Задать текущую конфигурацию рабочего стола). Все сохраненные конфигурации среды разработки хранятся в каталоге Delphi\Bin в файлах с расширением DST.

Основные части интегрированной среды разработки

Рис. 1.8. Окно настройки

Основные части интегрированной среды разработки

Рис. 1.9. Вкладка Commands
Вы можете сами указывать кнопки, которые будут располагаться на тех или иных панелях при помощи диалогового окна настройки Customize (рис. 1.8). Это окно можно вызвать из контекстного меню, щелкнув правой кнопкой в области панели управления, либо использовать пункт меню Delphi View/Toolbars/Customize (Просмотр/Панели управления/Настройка).
В окне настройки имеются три вкладки: Toolbars (Панели инструментов), Commands (Команды) и Options (Опции).
Вкладка Toolbars (рис. 1.8) позволяет показывать или скрывать вкладки панели инструментов.
На вкладке Commands (рис. 1.9) располагаются категории команд и сами команды, которые можно выбрать, щелкнув на названии команды, а затем на необходимой панели инструментов. В результате вышеописанных действий на выбранной панели инструментов появится новая кнопка. Для удаления ненужной кнопки с панели инструментов достаточно щелкнуть на этой кнопке левой кнопкой мыши и, удерживая ее, переместить на любое место экрана вне панелей инструментов.
Вкладка Commands очень удобное средство добавления практически любых команд на панель инструментов для быстрого доступа.
Наконец, вкладка Options (рис. 1.10) позволяет работать со всплывающими подсказками (tips) панелей инструментов.

Основные части интегрированной среды разработки

Рис. 1.10. Вкладка Options
Вы можете запретить или разрешить показывать подсказки (Show tooltips) при наведении курсора на любую кнопку панели инструментов. А также, имеется возможность разрешить или запретить показывать во всплывающих подсказках комбинации "горячих клавиш", выполняющих ту же функцию, что и кнопки на Панели инструментов.
Палитра компонентов сразу после установки Delphi состоит из девятнадцати вкладок, на которых расположены компоненты в виде значков. Каждая вкладка объединяет компоненты по назначению. Палитру компонентов можно настраивать по своему усмотрению с помощью окна Palette Properties (Свойства палитры). Открыть это окно можно при помощи контекстного меню, вызываемого щелчком правой кнопки на палитре компонентов или с помощью пункта меню главного окна Delphi Component/Configure Palette (Компонент/Конфигурация палитры). Палитра компонентов состоит из следующих вкладок:
- Standard (содержит стандартные компоненты, такие как кнопки, надписи и т. д.);
- Additional (дополнительные компоненты, такие как рисунок, маска ввода и т. д.);
- Win32 (доступ к 32-разрядному интерфейсу Windows);
- System (содержит системные компоненты, такие как таймер, медиаплейер и т. д.);
- Data Access (доступ к данным приложений баз данных с помощью BDE); - Data Controls (управление данными приложений баз данных);
- ADO (доступ к данным приложений баз данных с помощью ActiveX);
- Interbase (доступ к базе данных Interbase);
- Midas (работа с распределенными базами данных);
- InternetExpress (построение приложений, которые являются одновременно Web-сервером и распределенной базой данных);
- Internet (создание Web-серверов для сети Internet);
- FastNet (обеспечивает ряд протоколов для доступа к сети Internet);
- Decision Cube (обеспечивает многомерный анализ данных для приложений баз данных);
- QReport (визуальная разработка отчетов для приложений баз данных);
- Dialogs (компоненты для использования стандартных диалоговых окон Windows, такие как Save, Open, Print и т. д.);
- Win 3.1 (содержит компоненты управляющих элементов Windows 3.1 для совместимости приложений, написанных в ранних версиях Delphi под Windows 3.1);
- Samples (содержит примеры компонентов, которые могут быть построены разработчиком);
- ActiveX (содержит компоненты ActiveX);
- Servers (содержит компоненты для обычных СОМ-серверов).
Компоненты, расположенные на описанных выше вкладках, делятся на визуальные и невизуальные.
Визуальными называются компоненты, которые видны на форме во время выполнения приложения, а невизуальными - компоненты, которые отображаются на форме во время этапа проектирования в виде небольших значков и не отображаются на форме во время выполнения приложения.
Конструктор форм (рис. 1.11) при первоначальном запуске Delphi представляет собой обычное окно Windows, не содержащее никаких элементов. Это окно называется формой (Form). Разработчик размещает на форме необходимые компоненты из палитры компонентов простым перетаскиванием. С помощью мыши можно изменять размеры формы, а также ее положение на экране. Разрабатываемое приложение может содержать несколько форм (см. рис. 1.6).
Для настройки свойств, методов и событий компонентов используется инспектор объектов (Object Inspector) (рис. 1.12).
Инспектор объектов предназначен для выполнения трех основных функций: - установки свойств компонентов, размещенных на форме; - помощи в навигации и создании обработчиков событий; - фильтрации свойств и событий.

Основные части интегрированной среды разработки

Рис. 1.11. Конструктор форм

Основные части интегрированной среды разработки

Рис. 1.12. Инспектор объектов (окно Object Inspector, вкладка Properties)
Инспектор объектов состоит из списка объектов и вкладок свойств (Properties) и событий (Events).
Список объектов предназначен для быстрой навигации среди объектов и представляет собой выпадающий список, содержащий в себе все компоненты текущей формы. Активный компонент отображается в верхней строке выпадающего списка. На рис. 1.12 активным компонентом является сама форма Form 1.
Вкладка свойств (Properties) служит для установки необходимых свойств активного компонента во время проектирования. Установка свойств компонента во время проектирования задает начальные свойства объекта во время выполнения. Все свойства перечислены в левой части вкладки Properties, значения свойств расположены в правой части. Свойства могут быть отмечены значком "+", расположенным слева от названия свойства. При щелчке левой кнопкой мыши на этом значке откроются под свойства данного свойства. Значок "+" при этом сменится на "-".
Для возвращения свойства в исходное состояние (со знаком "+") достаточно щелкнуть на значке "-". Если значение свойства может быть установлено через стандартное диалоговое окно Windows, то рядом со значением свойст
ва при его выборе появляется кнопка "...", при нажатии на которую это диалоговое окно и вызывается. Оно также может вызываться при двойном щелчке мыши на значении свойства.
Если значение свойства является перечисляемым, то при выборе свойства появляется кнопка со стрелкой вниз, при нажатии на которую в выпадающем списке будут перечислены все значения, которые может принимать данное свойство. Выбор производится простым щелчком мыши по нужному значению.
Благодаря изменениям, которые были внесены разработчиками Delphi в API, отвечающей за создание свойств, вы можете видеть у некоторых свойств изображение прямоугольника, заполненного соответствующим рисунком. Например, рядом со свойством Color будет прямоугольник, закрашенный выбранным цветом. При создании собственных компонентов вы можете указать, что именно должно быть нарисовано в инспекторе объектов при задании определенного свойства вашего компонента.
Вы можете также группировать свойства по категориям, для этого нужно щелкнуть правой кнопкой мыши на свойствах и выбрать в выпадающем меню Arrange/by category (Группировать/по категории). При этом вкладка свойств (Properties) примет вид, как на рис. 1.13.

Основные части интегрированной среды разработки

Рис. 1.13. Свойства, сгруппированные по категориям
Вы можете просмотреть свойства той или иной категории, щелкнув на значке "+" слева от названия категории. Имеется возможность скрывать и отображать отдельные группы свойств, это делается следующим образом: щелкните правой кнопкой мыши на свойстве или группе свойств, выберите из выпадающего меню пункт View (Просмотр). Те группы свойств, которые отмечены флажком, являются видимыми. По умолчанию, группа свойств наследования Legacy не отображается.
Обратите внимание, что свойства для разных объектов могут довольно сильно отличаться. В Delphi имеется возможность множественного выбора объектов на форме (для этого необходимо, удерживая нажатой клавишу , последовательно щелкать на выбираемых объектах активной формы). При этом в окне Инспектора объектов будут отображаться свойства, которые присутствуют одновременно у всех этих объектов. Изменение значения какого-либо из свойств, приводит к изменению этого свойства у всех выбранных объектов.
Вкладка событий (Events) (рис. 1.14) содержит список событий (системных и пользовательских), которые могут произойти с объектом, выбранным в окне выбора объекта. Список событий отличается для разных объектов. Названия событий перечислены на правой части вкладки событий.
Название каждого события начинается с приставки
On.
Для того чтобы привязать к какому-либо событию процедуру-обработчик (event handler), достаточно сделать двойной щелчок мышью в правой части вкладки событий напротив выбранного события. При этом активируется окно редактора кода. При создании простейшего приложения мы рассмотрим, как создавать процедуры-обработчики.

Основные части интегрированной среды разработки

Рис. 1.14. Вкладка событий Events
При выборе любого события справа от него появляется выпадающий список, содержащий все доступные обработчики событий. Разработчик может выбрать любую из программ-обработчиков. Эта функция полезна, когда разработчик связывает одну процедуру-обработчик с несколькими событиями.
Редактор кода - это полноценный текстовый редактор, который включает в себя дополнительные функции:
- стилевое редактирование;
- проверку синтаксиса и цветовую подсветку команд; и многое другое.
Большинство команд редактора доступно в его контекстном меню. Для настройки редактора кода нужно выбрать в меню главного окна Delphi пункт Tools/Environment Options (Средства/Настройки среды).
Когда разработчик начинает новый проект, Delphi добавляет новую страницу (модуль) в редакторе кода для главной формы. По умолчанию ей присваивается имя Unit1.
Если в проекте присутствует несколько форм, то для каждой формы в редактор кода добавляется новый модуль (Unit2, Unit3 и т. д.). Разработчик может сам добавлять новые модули (Unit) при помощи пункта меню главного окна Delphi File/New/Unit (Файл/Новый/Модуль). При этом важно помнить, что для каждой формы Delphi автоматически создает модуль, т. е. форма обязательно связана с каким-либо модулем проекта. Если модуль был
создан разработчиком, то он является независимым и не связан ни с какой формой проекта. В такой модуль удобно выносить подпрограммы, используемые разными модулями проекта либо разными проектами. Редактор кода состоит из двух частей: окна просмотра и редактирования кода (Code Browser) и окна проводника (Code Explorer).

Основные части интегрированной среды разработки

Рис. 1.15. Редактор кода Code Editor
Окно просмотра и редактирования кода отображает весь программный код, связанный с текущим модулем. Это окно позволяет "на лету" проверять синтаксис вводимых команд, а также выводить подсказки. Настроить окно просмотра и редактирования кода можно с помощью пункта главного меню Delphi Tools/Editor Options (Средства/Настройки редактора). После выполнения этой команды появляется окно свойств редактора (Editor Properties) (рис. 1.16). Окно свойств редактора содержит пять вкладок: General (Общие), Display (Отображение), Key Mappings (Горячие клавиши), Color (Цвета), Code Insight (Выделение кода).
С помощью окна свойств редактора можно настроить практически все параметры отображения текстов программ в окне просмотра и редактирования кода, а также проверки синтаксиса.
Окно проводника служит для упрощения навигации между модулями проекта. Окно проводника можно закрыть, щелкнув левой кнопкой мыши в верх
нем правом углу окна проводника. С помощью пункта главного меню Delphi View/Code Explorer (Просмотр/Окно проводника) можно снова активировать это окно.

Основные части интегрированной среды разработки

Pис. 1.16. Окно свойств редактора Editor Properties
Окно проводника содержит дерево диаграмм, которые отображают все типы, классы, свойства, методы, глобальные переменные и глобальные программы определенные в модуле. Оно отображает также и другие модули, перечисленные после команды uses.
В. окне проводника для обозначения всего вышеперечисленного используются следующие значки (рис. 1.17).

Основные части интегрированной среды разработки

Рис. 1.17. Обозначения в окне проводника
Для перехода из окна проводника в окно просмотра и редактирования кода и наоборот, можно использовать комбинацию клавиш ++.
В окне проводника можно использовать пошаговый поиск. Чтобы найти любой класс, свойство, метод, переменную или программу (процедуру или функцию) достаточно набрать его имя на клавиатуре. Между окном проводника и окном просмотра и редактирования кода существует прямая связь. При выборе объекта в окне проводника курсор в окне просмотра и редактора кода сместится на раздел реализации (implementation) для данного объекта. И, наоборот, при перемещении в окне просмотра и редактирования кода будут подсвечиваться соответствующие объекты в окне проводника.
Для добавления (переименования) объекта в окне проводника достаточно щелкнуть правой кнопкой мыши в соответствующем узле окна проводника и выбрать в контекстном меню команду New/Rename (Новый/Переименовать).
Окно проводника также можно настроить с помощью окна настройки среды (Environment Options) (рис. 1.18). Оно вызывается с помощью пункта меню главного окна Delphi Tools/ Environment Options (Средства/Настройки среды).

Основные части интегрированной среды разработки

Рис. 1.18. Окно настройки среды Environment Options

Основные свойства визуальных компонентов Delphi

Свойства компонентов позволяют управлять внешним отображением компонентов, а также их поведением. Для установки большинства свойств компонентов можно воспользоваться инспектором объектов. Рассмотрим, какие основные свойства характерны для визуальных компонентов.

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

- Свойства, описывающие расположение и размеры компонентов (данные свойства недоступны для невизуальных компонентов):
Height (высота) - величина, описывающая вертикальный размер компонента;
Width (ширина) - величина, описывающая горизонтальный размер компонента;
Tор (вершина) - вертикальная координата левого верхнего угла компонента;
Left (левая граница) - горизонтальная координата левого верхнего угла компонента;
- свойства, описывающие визуальное отображение компонентов на экране:
BorderStyle (стиль бордюра) - определяет стиль бордюра, который будет отображен вокруг компонента;
Color (цвет) - определяет фоновый цвет компонента;
BevelKind (тип фаски) - определяет тип фаски компонента (выпуклый или вдавленный), если компонент ее поддерживает;
Font (шрифт) - определяет тип шрифта, используемого в текстах и надписях компонента, его цвет, стиль и размер;
- родительские свойства - свойства, которые наследуются от компонента, содержащего данный компонент. Такие свойства начинаются со слова Parent, например ParentFont, ParentshowHint и т. п. В качестве примера, рассмотрим кнопку Button, размещенную на форме Form. По умолчанию, свойство кнопки ParentFont установлено в true. В результате этого, при
изменении свойства Font на форме автоматически будет изменяться свойство Font кнопки Button. Если же вы замените свойство Font у кнопки, то шрифт, используемый для надписи на кнопке, изменится, а также свойство кнопки parentFont автоматически сменится на false;
- свойства навигации - определяют, как может пользователь во время работы приложения переходить от одного компонента к другому:
Caption (заголовок) - содержит строку текста, которая изображается на компоненте (маркирует его). Для того чтобы подчеркнуть букву в заголовке, можно поставить перед этой буквой знак амперсанда (&). В результате, пользователь может выбрать данный компонент путем нажатия комбинации клавиш <Аlt>+<подчеркнутая буква>;
TabOrder (порядок обхода компонентов) - данное свойство определяет порядковый номер компонента при обходе всех компонентов формы при помощи клавиши <Таb>. Это свойство является активным, только если свойство Tabstop установлено в true;
TabStop (обход по клавише <Таb>) - определяет, будет ли осуществляться последовательный обход компонентов при нажатии клавиши <Таb>. При установке данного свойства в true при нажатии клавиши <Таb> будет осуществляться обход компонентов формы в порядке, определенном свойствами TabOrder компонентов;
- свойства drug-and-drop - свойства, предназначенные для перемещения компонентов:
DragMode (режим перемещения) - определяет, каким образом начинается перемещение компонента. По умолчанию, данное свойство установлено в dmManual. Вы должны вызвать метод BeginDrag, для начала перемещения компонента. Когда вы устанавливаете свойство DragMode в dmAutomatic, перетаскивание компонента начинается сразу же по нажатию кнопки мыши на компоненте;
DragCursor (курсор при перетаскивании) - определяет вид указателя мыши, который он принимает при перетаскивании компонента.

Содержание Глава 2

Создание и уничтожение объектов

В процессе работы со стандартными объектами, которые предлагает Delphi, вы можете захотеть создать собственные объекты, которые наиболее полно описывают предмет вашей работы.
В качестве примера создания собственного объекта рассмотрим объект служащего вашей фирмы, и назовем его TEmployee. Предположим, что наш объект должен содержать такие свойства, как имя, должность и заработная плата, назовем их соответственно Name, Title и salary. А также добавим метод для расчета заработной платы: calculateSalary. Тогда описание нашего нового объекта получится похожим на следующее (листинг 1.5):

Листинг 1.5
type
TEmpioyee = class(TObject) private
FName: string;
FTitle: string;
FSalary: Double; public
property Name: string read FName write FName;
property Title:, string read FTitle write FTitle;
property Salary: Double read FSalary write FSalary;
function CaicuiateSaiary:
Double; end;

Первая строка в описании нового объекта говорит, что наш объект является наследником базового класса TObject, следовательно, все свойства и методы объекта TObject наследуются объектом TEmpioyee. Теперь вы можете помес
тить приведенный на листинге 1.5 код в любой из двух разделов модуля: раздел интерфейса (interface) или раздел реализации (implementation). После чего для создания объекта TEmployee вам потребуется вызвать метод Create, который наш объект унаследовал от базового объекта TObject:
var
Employee: TEmployee; begin
Employee := TEmployee.Create;
end;
Метод Create вызывает так называемый конструктор (constructor) объекта, который выделяет место в памяти для нового объекта и возвращает ссылку на созданный объект.

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

Для уничтожения созданного объекта и освобождения занимаемой объектом памяти необходимо вызвать метод Destroy, который также наследуется от базового объекта TObject. В нашем случае, строка для уничтожения объекта будет выглядеть так:
Employee.Free;

Стандартные компоненты Delphi

При знакомстве с палитрой компонентов мы с вами уже рассмотрели основные компоненты Delphi, которые разбиты на группы. Каждая группа компонентов предназначена для решения какой-либо задачи и располагается на отдельной вкладке палитры компонентов. Кратко мы уже описывали содержание каждой вкладки. Теперь рассмотрим стандартные компоненты Delphi, расположенные на некоторых вкладках, более подробно.
- Вкладка Standard (рис. 1.20) содержит следующие элементы:
Frames (фреймы) - открывает диалоговое окно, отображающее список фреймов, включенных в данный проект;
MainMenu (главное меню) - создает главное меню вашей формы. Для добавления элементов главного меню и создания обработчиков событий элементов меню разместите компонент MainMenu на форме и дважды щелкните по нему для открытия дизайнера меню (Menu Designer);
FopupMenu (контекстное меню) - создает контекстное меню, которое появляется во время выполнения приложения при нажатии пользова
телем правой кнопки мыши. Добавление элементов в контекстное меню осуществляется так же, как и в главное меню;
Label (метка) - служит для отображения текста, который не может быть изменен пользователем во время работы приложения;
Edit (поле редактирования) - отображает редактируемую область, в которую пользователь может ввести одну строку текста либо изменить существующую строку текста;
Memo (поле мемо) - отображает редактируемую область, в которую пользователь может ввести несколько строк текста или отредактировать существующие строки;
Button (кнопка) - создает кнопку, которую пользователь может выбрать для выполнения какого-либо действия;
checkBox (флажок) - предоставляет пользователю флажок, который может принимать значения "включено" или "выключено". Пользователь может включать одновременно несколько флажков;
RadioButton (переключатель) - создает переключатель, который работает по аналогии с флажком, за исключением того, что пользователь может включить только один переключатель на форме;
ListBox (список) - отображает прокручиваемый список элементов;
ComboBox (комбинированный список) - создает элемент, являющийся комбинацией поля редактирования и списка. Пользователь может ввести необходимые данные в область поля редактирования или выбрать элемент списка;
ScrollBar (полоса прокрутки) - создает инструмент, предназначенный для смены видимой области формы или списка;
GroupBox (группа) - создает контейнер, в котором можно размещать компоненты, которые будут объединены в группу;
RadioGroup (группа переключателей) - создает группу, которая содержит радиопереключатели;
Panel (панель) - создает панель, которая может содержать различные компоненты. Панели предназначены для создания статусных панелей или панелей инструментов;
ActionList (список действий) - создает список действий, которые централизуют ответы на действия пользователя.

Стандартные компоненты Delphi

Рис. 1.20. Вкладка Standard
Рис. 1.21. Вкладка Additional
- Вкладка Additional (рис. 1.21) состоит из нижеперечисленных компонентов:
BitBtn (кнопка с рисунком) - создает кнопку, которая может содержать рисунок;
SpeedButton (кнопка быстрого вызова) - создает кнопку, которая может содержать только рисунок (без текста). Данные кнопки могут быть размещены на панели для создания панели инструментов;
MaskEdit (поле редактирования по шаблону) - создает элемент управления, который позволяет пользователю вводить данные так же, как и в поле редактирования, только с использованием маски ввода (ввод по заранее заданному шаблону);
StringGrid (таблица строк) - создает таблицу, в строках и столбцах которой могут находиться текстовые строки;
DrawGrid (таблица) - создает таблицу для отображения каких-либо данных;
Image (рисунок) - служит для отображения содержимого файлов рисунков, значков или метафайлов;
Shape (геометрическая фигура) - предназначен для рисования геометрических фигур (эллипсов, окружностей, прямоугольников, квадратов, прямоугольников и квадратов со скругленными углами);
Bevel (фаска) - создает линию или квадрат с трехмерным эффектом (выпуклости или вогнутости);
ScrollBox (область прокрутки) - создает контейнер, который может изменять размеры. Область прокрутки автоматически отображает полосы прокрутки, если в них возникает необходимость;
CheckListBox (список флажков) - отображает прокручиваемый список, похожий на обычный список, за исключением того, что в списке флажков напротив каждого элемента списка установлен или отключен флажок выбора;
Splitter (разделитель) - отображает разделитель между двумя выровненными элементами управления, который позволяет пользователю во время работы приложения изменять размеры этих элементов управления, щелкая на разделителе и перетаскивая его;
StaticText (статический текст) - компонент, похожий на метку, за исключением того, что у статического текста есть собственный обработчик;
ControlBar (контрольная панель) - компонент, предназначенный для создания плавающей панели инструментов;
ApplicationEvents (события приложения) - компонент, позволяющий перехватывать события приложения;
Chart (диаграмма) - диаграмма - графический аналог таблицы (см. далее).
- Вкладка Win32 (рис. 1.22) содержит следующие компоненты:
TabControl (вкладки) - предназначен для размещения нескольких переключаемых страниц, содержащих различные элементы (например, панели инструментов Delphi выполнены в виде вкладок);
PageControl (страницы) - используется для создания многостраничных диалоговых окон (например, окно настройки проекта (Project Options) Delphi);
ImageList (список рисунков) - создает объединение рисунков, имеющих одинаковый размер, каждому из которых сопоставлен собственный индекс;
RichEdit (текстовый редактор) - создает поле мемо, представляющее из себя полноценный текстовый редактор в формате RTF (Rich Text Format), который, кроме самого текста, содержит элементы управления и свойства шрифтов, такие как размер, цвет, название и тип шрифта и т. п.;
TrackBar (бегунок) - панель, предназначенная для визуального изменения значения какой-либо переменной величины. Бегунок может быть горизонтальным или вертикальным;
ProgressBar (индикатор хода выполнения) - создает прямоугольную панель, которая заполняется слева направо для индикации хода выполнения какой-либо задачи;
UpDown (кнопки изменения величины) - создает кнопки, предназначенные для уменьшения или увеличения какой-либо переменной величины;
HotKey (горячие клавиши) - предназначен для создания комбинаций "горячих клавиш", вызывающих определенное действие;
Animate (анимация) - создает окно, позволяющее отображать беззвучный видеоклип формата AVI (Audio Video Interleaved);
DateTimePicker (комбинированный список ввода времени или даты) - создает элемент, предназначенный для ввода времени или даты пользователем, путем выбора либо печати;

Стандартные компоненты Delphi

Рис. 1.22. Вкладка Win32
MonthCalendar (календарь) - отображает календарь, который показывает только один месяц. Пользователь может выбрать нужную дату;
Treeview (дерево) - отображает список в виде дерева;
Listview (список) - отображает список;
Headercontrol (заголовок) - отображает заголовок над столбцами, содержащими текст или числа;
StatusBar (строка состояния) - создает панель, предназначенную для вывода служебной информации;
ToolBar (панель инструментов) - создает элемент, позволяющий размещать на себе кнопки и другие элементы, автоматически подбирая их размер и расположение;
CoolBar (усложненная панель) - отображает коллекцию элементов управления;
PageScroller (прокрутка страницы) - задает элемент, который содержит объекты клиентской области, которые могут прокручиваться вертикально или горизонтально.

Стандартные компоненты Delphi

Рис. 1.23. Вкладка System
- Вкладка System (рис. 1.23) содержит:
Timer (таймер) - невизуальный компонент, который генерирует событие OnTimer циклически через определенный промежуток времени;
PaintBox (окно для рисования) - размещает на форме область, в которой можно рисовать, используя графические средства Delphi;
MediaPlayer (медиапроигрыватель) - отображает панель для проигрывания и записи видео- и аудиофайлов;
OleContainer (контейнер OLE) - предназначен для создания клиента OLE;
DdeClientconv (клиентская связь DDE) - устанавливает клиентскую связь с сервером динамического обмена данными (Dynamic Data Exchange, DDE);
DdeClientItem (данные клиента DDE) - определяет данные клиента DDE, предназначенные для передачи серверу;
DdeserverConv (серверная связь DDE) - устанавливает связь сервера с клиентом DDE;
DdeServerItem (данные сервера DDE) - определяет данные сервера DDE, предназначенные для передачи клиенту.
Остальные вкладки (кроме тех, на которых расположены компоненты для работы с базами данных) мы рассмотрим непосредственно при работе с конкретными компонентами.

Ваше первое приложение в Delphi

Сейчас настало время создать первое приложение в среде Delphi. Для этого поместим на форму кнопку (Button1) простым перетаскиванием кнопки из палитры компонентов (вкладка Standard). В результате на форме появилась кнопка с названием Buttoni и такой же надписью (заголовком). Название кнопки и заголовок - разные вещи! Название (Name) любого объекта (и кнопки в том числе) указывает, как к этому объекту будут обращаться в дальнейшем. По умолчанию Delphi присваивает объектам собственные имена, которые можно (и обычно нужно) изменить. Имя объекта и заголовок по умолчанию также одинаковы и, в нашем случае, они имеют значение Buttoni. Изменить имя объекта можно через его свойство Name в инспекторе объектов, а заголовок через свойство caption. Давайте изменим заголовок кнопки на Выход. При изменении данного свойства в инспекторе объектов автоматически будет изменяться заголовок кнопки на форме. Также вы можете изменять и другие свойства кнопки. При этом автоматически будут происходить визуальные изменения кнопки на форме (конечно, если вы изменяете визуальные свойства).
Посмотрим, что произошло в редакторе кода. Во-первых, в списке используемых модулей (uses) появилась новая запись StdCtrls - стандартные элементы управления. Модули подключаются автоматически и не исключаются, если разработчик убирает объект из формы (попробуйте удалить созданную ранее кнопку Button1). Во-вторых, в редакторе кода добавилась еще одна строка:
Button1: TButton;
Она указывает, что созданная вами кнопка приобрела вид экземпляра класса TButton (описание объектов см. далее). Кроме того, описание данного экземпляра входит в описание формы TFom1, т. е. указывается на принадлежность кнопки к данной форме. Если вы захотите обратиться к этой кнопке из другой формы или другого модуля (unit), то вам потребуется ссылка с использованием имени экземпляра формы: Form1. Button1. При удалении кнопки из формы удалится и строка, описывающая кнопку.
Для того чтобы кнопка выполняла какие-либо действия при нажатии на нее, нужно выбрать вкладку событий (Events) в инспекторе объектов. Здесь перечислены события, которые могут произойти с кнопкой. Нас интересует событие OnClick. Щелкните дважды на данном пункте, при этом Delphi автоматически сгенерирует заготовку процедуры-обработчика и перенесет вас в окно редактирования программ (можно также дважды щелкнуть на кнопке в форме). Внесем в заготовку код для закрытия формы Formi:
Forml.Close;
Для того чтобы убедиться, что программа работает - запустите ее на исполнение с помощью клавиши или через меню Run/Run (Запуск/Запуск). Нажмите на единственную кнопку формы, и она (форма) закроется.

ActiveX в Delphi

Интерфейс IUnknown

По аналогии с наследованием классов, предком которых является базовый класс TObject, все интерфейсы - это прямые или косвенные наследники интерфейса IUnknown. Этот базовый интерфейс описан в модуле System следующим образом (листинг 1.16):

Листинг 1.16
type
IUnknown = interface
['{ 00000000-0000-0000-С000-000000000046}']
function Querylnterfасе(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;7
end;

Синтаксис описания интерфейса похож на описание класса. Главное отличие заключается в том, что интерфейс может быть связан с глобальным уникальным идентификатором (Global Unique Identifier, GUID).
GUID - это 128-разрядное целое число, которое используется для уникальной идентификации интерфейсов. Так как в 128-ми разрядах можно закодировать большое количество чисел, GUID практически гарантирует глобальную уникальность идентификатора интерфейса. То есть, практически невозможно, чтобы на двух компьютерах GUID совпал. Алгоритм генерации GUID основан на аппаратной части компьютера (частота процессора, номер сетевой карты и т. д.). В результате работы алгоритма, который может быть реализован с помощью функции API CocreateGUID (), получается запись типа TGUID. Эту запись можно определить в виде строки следующего формата:
'{хххххххх-хххх-хххх-хххх-хххххххххххх}'
В дальнейшем, при рассмотрении СОМ, мы увидим, что каждый интерфейс или класс СОМ имеет собственный GUID. Для интерфейсов - это идентификатор интерфейса (Interface ID, IID), а для класса - идентификатор класса (Class ID, CLSID).
Для создания нового GUID в среде Delphi достаточно нажать комбинацию клавиш ++ в окне редактора кода.
Итак, интерфейс lunknown поддерживает три метода, которые наследуются всеми интерфейсами:
- QueryInterface() - используется для создания запроса, поддерживается ли данный интерфейс и если ответ положителен, то метод возвращает указатель на него. Для примера, предположим, что имеется некоторый объект object, который поддерживает несколько интерфейсов interface1, interface2 и др. Для получения указателя на интерфейс interface2, объекта Object, вам нужно вызвать метод Interface2.Query Interface О;
- _AddRef () - используется, когда получен указатель на данный интерфейс и вы хотите работать с этим указателем. Метод _AddRef() обязательно должен заканчиваться вызовом метода _Release ();
- _Release () - данный метод применяется для завершения работы с интерфейсом.
Интерфейсы являются фундаментальными элементами таких распределенных объектных моделей, как СОМ и CORBA.
Более подробно интерфейс lunknown мы рассмотрим в третьей части книги, посвященной использованию технологий СОМ и ActiveX.

Интерфейсы

и их совместное использование классами
Ключевое слово Delphi interface позволяет создавать и использовать интерфейсы в ваших приложениях. Интерфейсы служат для расширения модели наследования в VCL, позволяя одному классу принадлежать нескольким
интерфейсам, а также нескольким классам - наследникам различных базовых классов использовать один интерфейс. Интерфейсы полезны в тех случаях, когда наборы операций, такие как потоки, используются большим количеством объектов.
Таким образом, интерфейсы - это средства для обеспечения взаимодействия между разными объектами.
Интерфейсы являются фундаментом для технологий компонентной объектной модели (СОМ) и CORBA.
Интерфейсы похожи на классы, которые содержат в себе только абстрактные методы и четкие определения их функциональности. Определение метода интерфейса включает в себя параметры и типы параметров, возвращаемый тип, а также ожидаемое поведение. Методы интерфейса семантически или логически связаны с отражением цели интерфейса. Существует соглашение об интерфейсах, гласящее, что каждый интерфейс должен быть назван в соответствии с задачей, которую он будет выполнять. Например, интерфейс iMalloc предназначен для распределения, освобождения и управления памятью. Аналогично, интерфейс IPersist может использоваться как базовый интерфейс для потомков, каждый из которых определяет специфичные прототипы методов для загрузки и сохранения состояния объектов в память, поток или в файл. Приведем простой пример объявления интерфейса (листинг 1.14):

Листинг 1.14
type
IEdit = interface
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;

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

Нельзя создать экземпляр интерфейса при помощи интерфейса. Для получения экземпляра интерфейса вам нужно объявить его в классе, содержащем данный интерфейс. Таким образом, нужно определить класс, который содержит необходимый интерфейс в списке своих родителей (листинг 1.15).

Листинг 1.15
TEditor = class(TInterfacedObject, lEdit)
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;

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

Использование интерфейсов в распределенных приложениях

Интерфейсы являются фундаментальным элементом распределенных объектных моделей СОМ и CORBA (более подробно о моделях читайте в третьей части книги). Delphi обеспечивает базовые классы для этих технологий, которые расширяют возможности объекта TInterfacedObject.
Классы СОМ добавляют возможности использования фабрик классов и идентификаторов классов (CLSID). Фабрики классов отвечают за создание экземпляров классов посредством CLSID. В свою очередь, CLSID используются для регистрации и манипуляции классами СОМ. Классы СОМ, которые обладают и фабрикой класса, и идентификатором класса, называются CoClasses. CoClasses имеют преимущество перед Querylnterface по части поддержки новых версий интерфейсов. Новые версии старых интерфейсов автоматически становятся доступными для программ-клиентов. В приложе ниях СОМ разработчик может вносить правку в код интерфейса для улучшения работы приложения, не изменяя клиентской части кода.
Другая распределенная технология называется CORBA (Архитектура Брокера Общих Объектных Запросов, Common Object Request Broker Architecture). Описание данной технологии не входит в эту книгу, заметим только, что она позволяет создавать приложения для взаимодействия с различными аппаратными или программными платформами. Так, клиентское приложение CORBA, работающее в операционной системе Windows 98, также легко будет работать с сервером приложений операционной системы UNIX.

Глава 1 Содержание Глава 3

Использование ключевого слова implements

Многие классы VCL Delphi имеют в качестве некоторых своих свойств объекты. Кроме того, вы можете использовать в качестве свойств класса интерфейсы. В том случае, когда свойство имеет тип интерфейса, то вы можете использовать ключевое слово implements для определения методов, которые данный интерфейс передает объекту. По умолчанию, ключевое слово implements передает все методы интерфейса. Тем не менее, вы можете самостоятельно определить список тех методов интерфейса, которые передаются объекту.
На приведенном ниже листинге 1.18 представлен пример использования ключевого слова implements при создании объекта адаптера цвета, предназначенного для преобразования восьмибитного значения цвета RGB.

Листинг 1.18
unit cadapt;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
IRGBSbit = interface
['{Id76360a-f4f5-lldl-87d4-00c04fbl7199}']
function Red: Byte;
function Green: Byte;
function Blue: Byte;
end;
IColorRef = interface
[41d76360b-f4f5-lldl-87d4-00c04fbl7199}'] function Color: Integer; end;
TRGBSColorRefAdapter = class(TInterfacedObject, IRGBSbit, IColorRef) private
FRGBSbit: IRGBSbit;
FPalRelative: Boolean; public
constructor Create(rgb: IRGBSbit);
property RGBSIntf: IRGBSbit read FRGBSbit implements IRGBSbit;
property PalRelative: Boolean read FPalRelative write-FPalRelative;
function Color: Integer; end;
implementation
constructor TRGBSColorRefAdapter.Create(rgb: IRGBSbit);
begin
FRGBSbit := rgb; end;
function TRGBSColorRefAdapter.Color: Integer;
begin
if FPalRelative then
Result := PaletteRGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue) else
Result := RGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue);
end;
end.


Использование оператора as

Объекты, поддерживающие интерфейсы, могут использовать оператор as для динамического присоединения интерфейса. Например,
procedurePaintObjecta(P: TInterfacedObject) var
X: IPaint; begin
X := P as IPaint;
{операторы}
end;
В этом примере переменная Р имеет тип Tinterfacedobject. Данная переменная может быть назначена переменной х, как ссылка на интерфейс IPaint.. Для такого назначения компилятор генерирует код для вызова метода Querylnterface, относяшегося к Интерфейсу IUnknown переменной Р. Подобное назначение возможно, даже если Р не поддерживает данный интерфейс. То есть, компилятор не выдаст ошибку при таком назначении.
Во время выполнения вышеприведенного примера либо успешно происходит присваивание
Х:= Р as IPaint;
либо генерируется исключительная ситуация.
При использовании оператора as вы должны выполнять следующие требования:
- при объявлении интерфейса, явно объявляйте в качестве предка интерфейс lunknown. Так как только в этом случае вы сможете воспользоваться оператором аs;
- если вы используете оператор as для интерфейса, данный интерфейс должен иметь свой IID. Напомним, что для создания нового IID достаточно, находясь в редакторе кода, использовать комбинацию клавиш ++.

Класс TlnterfacedObject

В VCL Delphi определен класс TlnterfacedObject, который служит базовым классом для объектов интерфейса. Данный класс определен в модуле Delphi system следующим образом (листинг 1.17):

Листинг 1.17.
type
TlnterfacedObject = class(TObject, IUnknown) private
FRefCount: Integer;
protected
function Querylnterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall; public
property RefCount: Integer read FRefCount;
end;

Как мы видим, данный класс в качестве родителей имеет класс TObject и интерфейс lunknown. Класс Tinterfacedobject позволяет достаточно легко создавать классы, поддерживающие интерфейсы. Например,
type
TMyObjInterfaced = class(TInterfacedObject, IPaint)
end;
На вышеприведенном примере мы определяем новый класс TMyObj interfaced, который является прямым потомком класса Tinterfacedobject и поддерживает некий интерфейс IPaint.

Обработка RTL-исключений. Иерархия исключений

RTL (real time library)-исключения определены в модуле Delphi sysutils и являются наследниками базового класса исключений Exception.
Имеется несколько типов исключений-наследников RTL (табл. 1.1).
Таблица 1.1. Типы исключений из RTL
Тип ошибки

Причина

Значение

Ввод/вывод

Ошибка доступа к файлу или устройству ввода/вывода

Большинство исключений ввода/вывода связано с кодом ошибки, возвращаемом Windows при обращении к файлу

Куча

Ошибка использования динамической памяти

Ошибки кучи возникают при недостатке памяти или когда в приложении присутствует указатель на область памяти вне кучи

Целочисленные математические операции

Неправильное действие с выражением целого типа

Ошибки включают в себя: деление на ноль, переполнение, выход за пределы диапазона и др.

Математические операции с плавающей точкой

Неправильное действие с выражением вещественного типа

Ошибки с вещественными числами могут исходить от математического сопроцессора или программного эмулятора. Ошибки включают в себя: неправильные инструкции, деление на ноль, переполнение и др.

Операция аs

Неправильная работа с классами при помощи операции as

Объекты могут работать только с совместимыми объектами

Преобразование

Неправильное преобразование типов

Функции преобразования типов (IntToStr, StrToInt И др.) генерируют эту ошибку в случае невозможности преобразования

Аппаратные

Системные условия

Аппаратные ошибки указывают, что или процессор, или пользователь сгенерировал ошибку: доступа, переполнения стека или другую

Тип Variant

Неправильное использование типа

Variant

Ошибка возникает в выражениях, где не может использоваться тип

Variant


Рассмотрим теперь иерархию классов исключений .более подробно:
Exception

- базовый класс исключений

EAbort

- исключение для намеренного прерывания вычислений

EAbstractError

- попытка вызова абстрактного метода

EAccessViolation

- ошибка доступа к памяти

EArrayError

- ошибка при работе с массивами

EAssertionFailed

- ошибка при проверке истинности

EBitsError

- ошибка доступа к массиву булевых величин TBits

ECacheError

- ошибка построения кэша

EComponentError

- ошибка регистрации или переименования компонента

EControlC

- нажатие пользователем клавиш + при выполнении консольного приложения

EConvertError

- ошибка преобразования строк (объектов)

EDatabaseError

- ошибка работы с базами данных

EDBClient

- ошибка в наборе данных клиента

EReconcileError

- ошибка обновления данных компонента

TClientDataset

EDBEngineError

- ошибка в BDE

ENoResultSet

- генерируется компонентом TQuery при попытке открыть запрос без select

EUpdateError

- ошибка при обновлении В TProvider

EDateTimeError

- ошибка ввода даты или времени

EDimensionMapError

- ошибка формата данных в кубе решений

EDimlndexError

- ошибочный индекс в задании размерности в кубе решений

EExternalException

- неизвестное исключение

EInOutError

- ошибка ввода/вывода в файл

EIntError

- базовый класс исключений целочисленных математических операций

EDivByZero

- ошибка деления на ноль

ERangeError

- значение или индекс вне допустимого диапазона

EIntOverflow

- переполнение

EIntfCastError

- ошибочное преобразование типов as к интерфейсу

EInvalidGraphic

- нераспознаваемый графический файл

EInvalidGraphicOperation

- ошибка при операциях с графикой

EInvalidGridOperation

- ошибка при работе с сеткой (Grid)

EInvalidOperation

- ошибочная операция с компонентом

EInvalidPointer

- ошибка при операциях с указателем

EListError

- ошибка при работе со списком

ELowCapacityError

- ошибка выделения памяти для куба решений

EMathError

- базовый класс исключений операций с плавающей запятой

EInvalidArgument

- недопустимое значение параметра при обращении к математической функции

EInvalidOp

- неопределенная операция

EOverflow

- ошибка переполнения

EUnderflow

- потеря значащих разрядов

EZeroDi'vide

- ошибка деления на ноль

EMCIDeviceError

- ошибка доступа к устройствам через драйвер MCI (Media Control Interface)

EMenuError

- ошибка при работе с элементами меню

EOleCtrlError

- ошибка при связывании приложения с элементом ActiveX

EOleError

- низкоуровневая ошибка OLE

EOleSysError

- ошибка интерфейса OLE IDispatch

EOleException

- ошибка OLE, связанная со свойством или методом

EOutlineError

- ошибка при работе с Tout line

EOutOfMemory

- ошибка распределения памяти

EOutOfResources

- ошибка создания обработчика Windows

EPackageError

- исключение, генерируемое при загрузке или использовании пакета

EParserError

- ошибка преобразования текста описания формы в двоичный формат

EPrinter

- ошибка печати

EPrivelege

- ошибка выполнения инструкции процессора из-за нехватки привилегий

EPropReadOnly

- ошибка записи с помощью OLE значения свойства, предназначенного только для чтения

EPropWriteOnly

- ошибка чтения с помощью OLE значения свойства, предназначенного только для записи

EPropertyError

- ошибка при задании значения свойства

ERegistryException

- ошибка при работе с реестром Windows

EReportError

- ошибка задания типа сервера (компонент TReport не может соединиться с базой данных)

EResNotFound

- ошибка загрузки файла ресурсов (*.DFM или *.RES) во время создания приложения

EStackOverflow EStreamError

- переполнение стека - базовый класс исключений ошибок потоков

EFCreateError

- ошибка создания файла

EFOpenError

- ошибка открытия файла

EFilerError

- базовый класс исключений файловых потоков

EReadError

- ошибка чтения заданного числа байт

EWriteError

- ошибка записи заданного числа байт

EclassNotFound

- ошибка связи компонента с приложением

Elnvalidlmage

- ошибка чтения файла ресурсов

EmethodNotFound

- не найден метод

EStringListError

- ошибка доступа к окну списка

EThread

- ошибка многопоточного приложения

ETreeViewError

- ошибка индекса при работе с TTreeview

EVariantError

- ошибка при работе с типом данных variant

EWin32Error

- внутренняя ошибка Windows


Итак, класс Exception является базовым классом всех исключений в Delphi. Все вышеописанные классы являются прямыми или косвенными наследниками класса Exception. При создании собственных новых классов исключений необходимо использовать класс Exception как родительский. Только в этом случае Delphi гарантированно распознает и обработает новый класс как исключение. В свою очередь, класс Exception является прямым наследником базового класса TObject, и наследует все его функции. В отличие от
других классов, классы исключений начинаются не с буквы т, а с буквы Е. При создании собственных классов исключений можно называть их по своему усмотрению, не обязательно начиная с буквы Е (но это считается плохим стилем программирования).

Понятие исключительной ситуации, ее обработка средствами Delphi

Под исключительной ситуацией мы будем понимать некое непредвиденное событие, способное повлиять на дальнейшее выполнение программы.
При обработке такой ситуации Delphi, как обычно, работает с объектами. С точки зрения компилятора Delphi исключительная ситуация - это объект. Для работы с этим специфичным объектом в Delphi (точнее, в Object Pascal) были введены следующие языковые конструкции: try .. except и try .. finally.
Рассмотрим эти языковые конструкции более подробно.
Итак, конструкция try .. except имеет следующий синтаксис (листинг 1.6):

Листинг 1.6
try
{исполняемый код};
except
on Exceptionl do {код, исполняемый в случае возникновения ошибки 1};
on Exception2 do {код, исполняемый в случае возникновения ошибки 2};
else
{код, обработчик всех не перехваченных ранее ошибок};
end;

Если при выполнении кода, размещенного в разделе try, генерируется исключение, то выполнение этого раздела прекращается и управление передается коду, размещенному в разделе except. Раздел except может использоваться двумя способами. Во-первых, в нем могут располагаться любые операторы, кроме обработчиков исключений, начинающихся с приставки on. Это и операторы сообщения об ошибке, и команды, позволяющие освобождать системные ресурсы, а также другие операторы и команды. Во-вторых, раздел except используется для обработки исключений. В этом случае в него могут включаться только операторы обработки исключений. Если среди обработчиков встретился обработчик, соответствующий сгенерированному исключению, то выполняется оператор этого обработчика, исключение разрушается и управление передается коду, расположенному после оператора on Exception do. Раздел, расположенный после ключевого слова else, служит для обработки любых исключений, не описанных в разделе except. Этот раздел не является обязательным. Если при обработке исключительной ситуации не будет найден подходящий обработчик, то произойдет обработка системным обработчиком исключений.
Рассмотрим простой пример обработки исключительной ситуации деления на ноль (листинг 1.7).

Листинг 1.7
try
а:=10;
b:=0;
c:=a/b;
except
on EZeroDivide do MessageBox('Делить на ноль нельзя!');
end;

Итак, как можно видеть из приведенного выше примера, для обработки разных исключений служат разные операторы. Рассмотрим более подробно оператор обработки on .. do. Данный оператор находится внутри раздела except и может иметь две формы (листинг 1.8).

Листинг 1.8
on <класс исключения> do <оператор>;
или
on <имя>: <класс исключения>
do <операторы, в которых можно использовать свойства исключения>

Этот оператор обрабатывает только тот класс исключений, который в нем указан. При указании родительского (базового) класса, все классы исключений - потомки данного класса - также будут обработаны. Для обработки всех исключений можно обратиться к базовому классу всех исключений: Exception. После обработки исключения оно разрушается. Вторая форма оператора on .. do отличается от первой тем, что данному исключению можно временно присвоить имя и обращаться к свойствам исключения. Обращаться к свойствам исключения можно с помощью конструкции <имя>.<имя свойства>. Посмотрим листинг 1.9.

Листинг 1.9
try
ScrollBarl.Max := ScrollBarl.Min - 1;
except
on E: EInvalidOperation do
MessageDlg( 'Игнорируем исключение: '- + E.Message, mtlnformation, [mbOK], O)
end;

В приведенном примере мы присваиваем исключению EInvalidOperation временное имя Е. Затем в окне сообщения выводим текст ошибки E.Message, выдаваемый Delphi по умолчанию (если бы не было нашего обработчика ошибки).

Примечание
Никогда не уничтожайте временный объект исключения. Обработчик исключений автоматически уничтожает объекты исключения. Если вы уничтожите объект исключения самостоятельно, это может привести к ошибке доступа.

Иногда, бывает необходимо, чтобы после обработки исключительной ситуации своим кодом вызывался стандартный обработчик ошибки. Например, в случае возникновения некоторой ошибки вы хотите, чтобы приложение сообщало пользователю какую-либо информацию, а затем передавало управление стандартному обработчику ошибок. Как вы уже знаете, после обработки исключения вашим кодом, исключение уничтожается. Для того чтобы самостоятельно вызвать снова это исключение, можно воспользоваться регенерацией исключений. Для регенерации исключения служит команда raise. Рассмотрим листинг 1.10.

Листинг 1.10
try
{ операторы }
except
on <класс исключения> do
begin
{операторы обработки исключения}
raise; // Регенерация исключения
end;
end;

После выполнения операторов обработки исключения, написанных программистом, выполняется команда raise, которая снова принудительно вызывает это исключение, после чего управление передается стандартному обработчику исключений.
В случае, когда исключение успешно проходит через все блоки try в коде приложения, вызывается метод HandleException. Он показывает диалоговое окно ошибки. Вы можете вызвать этот метод так, как в листинге 1.11.

Листинг 1.11
try
{ операторы } except
Application.HandieException(Self);
end;

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

Листинг 1.12
try
{операторы, способные создать исключительную ситуацию};
finally
{защищенные операторы, выполняемые в любом случае};
end;

Итак, операторы, которые размещены после ключевого слова finally, будут выполняться в любом случае, была сгенерирована исключительная ситуация или нет. Если в разделе try была сгенерирована исключительная ситуация, то управление немедленно передается разделу finally. Также, если исключительной ситуации в разделе, try не было, блок finally будет выполняться. Даже если в разделе finally произойдет ошибка, выполнение операторов этого раздела будет продолжено до конца. В конструкции try .. finally не происходит обработка исключений, она используется в основмом для освобождения ресурсов памяти, закрытия ненужных файлов и других операций освобождения ресурсов. Таким образом, в данной конструкции нуждаются операции с файлами, памятью, ресурсами Windows и объектами.
Код обработки исключения можно разбить на блоки try .. except .. end и try .. finally .. end. Эти блоки могут быть вложенными (рис. 1.24, а и б).
При разработке приложений на Delphi часто возникают ситуации, когда программисту не требуется обрабатывать исключения, а необходимо лишь прервать нежелательное действие, вызывающее ошибку. Для этого применяются так называемые молчаливые исключения (silent exceptions). Молчаливые исключения являются потомками стандартного исключения EAbort. По умолчанию обработчик ошибок VCL Delphi отображает на экране диалоговое окно ошибки для всех исключений, кроме наследников EAbort.

Примечание
При создании консольных приложений сведения об ошибке выводятся и для необработанных исключений EAbort.

Для того чтобы сгенерировать молчаливое исключение, можно вызвать процедуру Abort. Она автоматически сгенерирует исключение EAbort, которое прервет текущую операцию без вывода сведения об ошибке на экран. Рассмотрим пример. Пусть форма содержит пустой список (ListBoxi) и кнопку (Button1). Запишем в обработчик события кнопки onclick следующий код:

Листинг 1.13
procedure TForml.ButtonlClick(Sender: TObject);
var
I: Integer;
begin
for I := 1 to 10 do (цикл 10 раз}
begin
ListBoxl.Items.Add(IntToStr(I)); {добавляем номер в список}
if I = 7 then Abort; {прерываем добавление номеров в список после добавления седьмого номера}
end;
end;

В результате работы программы, после нажатия кнопки Button1, в список будет добавлено семь строк с номерами от 1 до 7.

Понятие исключительной ситуации, ее обработка средствами Delphi

Рис. 1.24. Вложенные блоки в обработчике исключений (а) и в конструкции защиты кода (б)

Создание собственных исключений

Для создания собственных типов исключений необходимо знать, как определить тип объекта исключения, а также как вызвать исключение.
Так как исключение является объектом Delphi, то определение нового типа исключения так же nporfro, как определение объекта нового типа. Теоретически возможно вызывать любой объект как объект исключения, но стандартные обработчики исключений работают только с теми объектами, предками которых являются Exception или потомки Exception.
В качестве примера создания собственного типа исключения рассмотрим следующее определение:
type
EMyException = class(Exception);
Теперь, если вы вызовете исключение EMyException, но не напишете обработчик для него, то произойдет вызов стандартного обработчика для Exception. Так как стандартный обработчик для Exception показывает имя вызванного исключения, вы можете увидеть имя вашего нового исключения.
Для вызова созданного исключения используйте команду raise. Для примера рассмотрим типичную задачу проверки введенного пользователем пароля:
type
EPasswordlnvalid = class (Exception);
После определения нового типа исключения EpasswordInwalid вы можете вызвать это исключение в любом месте программы:
if Password <> CorrectPassword then raise EPasswordlnvalidCreate('Введен неправильный пароль');
Вызов созданного исключения производится по его имени.

ActiveX в Delphi

Инициализация потоков

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

Использование главного VCL-потока

Метод Execute является потоковой функцией. Вы можете представить поток как приложение, которое запускается из вашего приложения, за исключением одной детали: и ваше приложение, и поток находятся в одном процессе. Написание потоковой функции немного сложнее, чем .создание обычного приложения, т. к. вам придется следить за тем, чтобы разные потоки вашего приложения не пересекались в оперативной памяти. С другой стороны, поскольку потоки находятся в одном процессе, вы можете использовать память потоками совместно, для их взаимодействия.
Когда вы используете в своем приложении объекты из библиотеки визуальных компонентов, то вам нужно иметь в виду, что свойства и методы объектов из VCL не гарантируют потоковую безопасность.
В случае, когда все объекты обращаются к своим свойствам и выполняют свои методы в одном потоке, вам не нужно беспокоиться о том, что объекты будут создавать друг другу помехи.
Для использования главного VCL-потока создайте отдельную процедуру, которая выполняет необходимые действия. А затем вызовите эту процедуру из вашего потокового модуля (внутри метода Execute), используя синхронизацию. Например:
procedure TMyThread.PushTheButton;
begin
Buttonl.Click;
end;
procedure TMyThread.Execute;
begin
...
Synchronize(PushTheButton);
...
end;
Метод синхронизации защитит ваше приложение от ситуации "гонки".
Ситуация "гонки" возникает, когда два или более потока пытаются получить доступ к общему ресурсу и изменить его состояние.

Примечание
Метод синхронизации не может использоваться в консольных приложениях. Для защиты одновременного доступа к VCL-объектам в консольных приложениях вы должны использовать другие методы.

В некоторых случаях вы можете обходиться без метода синхронизации, например:
- компоненты доступа к данным (Data access) являются потокобезопасными в том случае, когда каждый поток обращается к собственной базе данных. Есть одно исключение, когда вы пытаетесь получить доступ к базе данных Microsoft Access. Access работает с библиотекой Microsoft ADO, которая не является потокобезопасной.

Примечание
Если вы используете компоненты доступа к данным, вы должны задействовать синхронизацию в том случае, когда, например, устанавливаете связь между компонентами доступа к данным (например, определяете свойство DataSet в объекте DataSource). Но вы можете не прибегать к синхронизации при обращении к данным таблицы базы данных.

- объекты для работы с графикой являются потокобезопасными. Это такие классы, как TFont, TPen, TBrush, TBitmap, TMetaf ile И Ticon;
- вместо объектов списков (List), которые не являются потокобезопасными, вы можете использовать потокобезопасный потомок объекта TList - TThreadList.

Использование критической секции

Если в вашем приложении имеются объекты, которые не являются потоко-безопасными, Delphi позволяет использовать так называемые критические секции.
Критическая секция - это область глобальной памяти, которую необходимо защищать от одновременного использования ее разными потоками.
Критическая секция работает наподобие "ворот", которые закрываются для всех потоков в то время, когда один поток "зашел" в эти "ворота". Она позволяет работать с данной областью памяти только одному потоку, блокируя доступ всех остальных потоков.
Для использования критической секции в своем приложении вы должны создать глобальный экземпляр класса TCriticaisection. Данный класс имеет несколько методов, из которых для нас важны два: Acquire (или Enter) и
Release (ИЛИ Leave).
Метод Acquire (Enter) связывает критическую секцию с потоком, вызвавшим данный метод, блокируя доступ к этой секции всем остальным потокам.
Метод Release (Leave) снимает блокировку и позволяет другим потокам обращаться к критической секции.

Примечание
При использовании критических секций будьте внимательны, т. к., если в вашем приложении несколько потоков имеют доступ к критической секции, они все должны работать с методом Acquire (Enter) для обращения к ней. Иначе может возникнуть проблема совместного доступа к ресурсу.

Для примера рассмотрим небольшую программу, которая содержит глобальную переменную критической секции LockXY. Данная переменная блокирует доступ к глобальным переменным X и Y.
LockXY.Acquire; // блокирование других потоков
try
Y := sin(X); finally
LockXY.Release;
end;
Любые потоки, которые используют глобальные переменные X или Y, должны содержать примерно такой же код.

Использование потоков в распределенных приложениях

Распределенные приложения добавляют головной боли программисту, который создает приложения, использующие потоки. Когда вы задумываетесь над тем, как вам синхронизировать выполнение потоков внутри приложения - это еще полбеды. При создании распределенных приложений вам придется решить вопрос: как другие процессы смогут повлиять на выполнение потоков вашего приложения.
Обычно, ответственность за обработку распределенных потоков лежит на приложении-сервере. Когда вы создаете серверы, вам придется решить этот вопрос.
Если каждый запрос приложения-клиента содержится в отдельном потоке, вы должны убедиться, что различные клиентские потоки не мешают друг другу.
Все эти вопросы мы рассмотрим в третьей части книги, когда будем создавать приложения на основе СОМ и ActiveX.

Глава 2 Содержание Глава 4

Координация потоков

При работе с потоками, на этапе первоначального знакомства с ними, неизбежны ошибки. Особенно неприятны ошибки, связанные с конфликтами потоков, обращающихся к разделяемым ресурсам, а также глобальным переменным. Иногда бывает необходимо, чтобы потоки работали слаженно, т. е., например, после выполнения какого-либо события в одном потоке вам необходимо, чтобы возникало определенное постоянное событие в другом потоке. Все это можно объединить под общим названием координации потоков. Рассмотрим, как можно эффективно управлять потоками для достижения решения практических задач.
Для начала определим, какие способы хранения локальных переменных потока нам предоставляет Delphi. Таких способов три:
- хранение локальных переменных в стеке потока. Так как любой поток приложения получает свой собственный стек, то он будет иметь собственные локальные переменные;
- сохранение локальных переменных в объекте потомка класса TThread;
- хранение локальных переменных на уровне операционной системы, используя в описании локальных переменных слово threadvar.
Первый способ является самым простым и очевидным, а также и самым эффективным. Доступ к локальным переменным, расположенным в стеке потока, самый быстрый.
Рассмотрим два других способа хранения локальных переменных потока. Второй способ проще и эффективнее, чем третий. Рассмотрим его на примере:
type
TMyThreadl = class(TThread)
private
i,j,k,1: integer; // локальные переменные потока типа integer
a,b,c: char; // локальные переменные потока типа char
end;
Эффективность объявления локальных переменных в потомке класса TThread очевидна, т. к. доступ к любому полю объекта осуществляется очень быстро (примерно в 10-11 раз быстрее, чем при использовании описания
threadvar).
Третий способ хранения локальных переменных (с помощью threadvar), служит для создания в потоках локальных копий глобальных переменных. Глобальные переменные могут использоваться всеми потоками приложения, при этом может возникнуть ситуация, когда при изменении глобальной переменной одним потоком происходит изменение этой же глобальной пере
менной другим потоком. В результате значение, установленное первым потоком, бесследно исчезает, приводя к нежелательным последствиям (в данном примере произойдет обработка ошибочного значения глобальной переменной первым потоком). Для исключения такой ситуации Win32 API предлагает средство хранения локальных данных потоков (thread-local storage). С помощью этого средства можно создавать локальные копии глобальных переменных для любого потока. При этом требуется всего лишь заменить одно слово var при объявлении глобальных переменных на слово threadvar.

Мьютексы (взаимные исключения)

Данный тип объекта ожидания разрешает доступ только одному потоку в определенный момент времени. Мьютекс сигнализирует, когда он не при надлежит потоку и прекращает сигнализировать, когда он принадлежит потоку. После того как поток перестает обращаться к мьютексу, любой другой поток получает возможность доступа к нему.
Данный объект полезен в случае обращения к памяти с целью ее изменения различными потоками.
Для создания объекта типа мьютекс потоки должны обращаться к функции CreateMutex!
CreateMutex (lpMutexAttributes, blnitialOwner, IpName);
где:
- IpMutexAttributes - указатель на структуру SECURITY_ATTRIBUTES, которая определяет, может ли обработчик наследоваться потоками-потомками. В Windows NT, при IpMutexAttributes = NULL, мьютекс получает дескриптор безопасности, установленный по умолчанию. В Windows 9x данный параметр функции игнорируется;
- blnitialOwner - определяет начального владельца мьютекса. Если данный параметр равен true, то владельцем мьютекса становится вызвавший его поток. В противном случае, владелец мьютекса не определен;
- lpName - определяет имя мьютекса. Имя предназначено для обращения к мьютексу из других процессов. Имя может содержать любые символы, кроме обратной косой Черты (\). Имя чувствительно к регистру букв. В случае, когда имя повторяет имя уже созданного мьютекса, происходит обращение к уже существующему мьютексу. Если значение имени равно NULL, то мьютекс создается без имени. Если мьютекс имеет такое же имя, как семафор, событие или другой объект, генерируется ошибка ERROR_INVALID_HANDLE.

Общий обзор потоков

Поток (Thread) - это объект операционной системы, заключенный в процесс и реализующий какую-либо задачу. Каждое приложение (процесс) Win32 имеет по крайней мере один поток, который называется главным (основным, стандартным). Каждый процесс может содержать несколько потоков.

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

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

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

Для того чтобы использовать объекты потоков в вашем приложении, вам нужно создать потомок класса TThread.
Класс TThread был создан для облегчения написания приложений с несколькими потоками. Он гарантирует совместимость при работе с библиотекой визуальных компонентов (VCL) Delphi.
Вообще, при создании многопоточных приложений необходимо следовать приведенным ниже рекомендациям:
- остерегайтесь создавать слишком много потоков - это может привести к большой загруженности операционной системы и процессора. Рекомендуемое ограничение числа активных потоков в одном процессе - 16 (для однопроцессорной системы);
- используйте синхронизацию в случае, когда несколько потоков пытаются получить доступ к одному ресурсу;
- большинство методов, которые обращаются к объектам VCL и изменяют содержимое формы, должны вызываться из главного VCL-потока или использовать объект синхронизации, такой как TMultiReadExclusive-WriteSynchronizer.
Определение объекта TThread находится в модуле classes и имеет следующий вид (листинг 1.19):

Листинг 1.19
TThread = class private
FHandle: THandle; FThreadID: THandle; FTerminated: Boolean; FSuspended: Boolean; FFreeOnTerminate: Boolean; FFinished: Boolean; FReturnValue: Integer; FOnTerminate: TNotifyEvent; Method: TThreadMethod; FSynchronizeException: TObject; procedure CallOnTerminate; function GetPriority: TThreadPriority; procedure SetPriority(Value: TThreadPriority); procedure SetSuspended(Value: Boolean); protected
procedure DoTerminate; virtual;
procedure Execute; virtual; abstract;
procedure Synchronize(Method: TThreadMethod);
property ReturnValue: Integer read FReturnValue write FReturnValue;
property Terminated: Boolean read FTerminated;
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
procedure Resume;
procedure Suspend;
procedure Terminate;
function WaitFor: LongWord;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Handle: THandle read FHandle;
property Priority: TThreadPriority read GetPriority write SetPriority; property Suspended: Boolean read FSuspended write SetSuspended; property ThreadID: THandle read FThreadID;
property OnTerminate: TNotifyEvent read FOnTerminate write FQnTerminate; end;

Из вышеприведенного листинга можно определить, что объект TThread является прямым потомком объекта TObject, следовательно, он не является визуальным компонентом. Его метод Execute - абстрактный, а значит, сам объект TThread тоже является абстрактным, и вы не сможете создать экземпляр этого класса. Таким образом, вам придется определять классы-потомки данного класса для работы с потоками.
Для создания потомка класса TThread выберите в главном меню Delphi команду File/New (Файл/Новый), затем в появившемся окне (рис. 1.25) щелкните на Thread Object (Объект потока).
В открывшемся диалоговом окне напишите имя для вашего нового объекта потока. После всего этого Delphi создаст новый модуль и в окне редактора кода появится новая вкладка.
Рис. 1.25. Добавление объекта потока в проект с помощью диалогового окна New Items

Примечание
В отличие от большинства диалоговых окон IDE, которые требуют ввода имени класса, диалоговое окно создания нового объекта потока не добавляет автоматически букву "Т" перед названием вашего класса. Поэтому желательно самостоятельно правильно называть новые потоки, например TMyThread.

Если вы проделали все вышеописанное и назвали новый объект потока TMyThread, то в новом модуле, сгенерированном Delphi, вы можете увидеть код - заготовку для вашего нового объекта потока (листинг 1.20).

Листинг 1.20
unit Unit2;
interface
uses
Classes; type
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implementation { TMyThread }
procedure TMyThread.Execute;
begin
{ Place thread code here } end;
end.

В автоматически сгенерированном файле модуля вы можете: - инициализировать поток;
- заполнить метод Execute объекта потока, разместив там функции и процедуры;
- написать код гарантированного разрушения потока (например, строку FreeOnTerminate:=True;).

Определение времени, занимаемого потоком

Кроме рассмотренных выше методов активизации и завершения потоков, используется то обстоятельство, что потоки могут приостанавливать и восстанавливать свою работу. Для этого используются методы Suspend и Resume.
Под управлением не многопоточной операционной системы определить количество времени, требуемого на выполнение каких-либо вычислений, достаточно просто. Для этого можно использовать функцию GetTickCount. Например, чтобы определить, сколько времени требуется для генерации случайного числа в Delphi, достаточно написания следующего кода:
Var
StartTime, Summa: longint;
R: integer;
begin
StartTime:=GetTickCount;
R:=Random(MaxInt) ;
Summa:=GetTickCount - StartTime;
end;
В многопоточной операционной системе узнать время, необходимое для выполнения каких-либо операций, труднее, т. к. вычисления вашего потока могут быть прерваны в любом месте для выполнения других потоков. Для этого в по-настоящему многопоточных операционных системах (например, Windows NT) имеется функция GetThreadTimes, которая предоставляет информацию о времени работы потока:
function GetThreadTimes (hThread: THandle; var IpCreationTime, IpExitTime, IpKernelTime, IpUserTime: TFileTime): BOOL; stdcall;
Рассмотрим параметры, передаваемые в данную функцию:
- hThread - название потока, для которого определяется время выполнения;
- IpCreationTime - время создания потока;
- lpExitTime - время завершения работы потока (определяется только при завершении работы потока);
- lpKerneiTime - время, затраченное операционной системой для выполнения собственного кода;
- lpUserTime - время, затраченное на выполнение приложений пользователя.
Как.мы видим, последние четыре параметра имеют тип TFiieTime:
type
TFiieTime = record
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end;
To есть, Элементы записи TFiieTime: dwLowDateTime И dwHighDateTime В сумме образуют 64-разрядное значение. Это значение определяет количество интервалов (каждый интервал равен 100 наносекунд), которые прошли начиная с 1 января 1601 года.

Примечание
Для работы с типом TFiieTime удобно использовать функцию приведения данного типа к типу Int64, например: If Int64 (CreationTime) > Int64 (ExitTime) then ShowMessage('Все в порядке.').


Ожидание завершения работы потока

К основным понятиям механизма синхронизации потоков относятся функции и объекты ожидания.
В Win32 API имеется несколько функций, называемых функциями ожидания, позволяющих приостановить работу потока до тех пор, пока не будет изменено состояние какого-либо объекта операционной системы, который является объектом ожидания.
К таким объектам относятся следующие объекты ядра Win32 API:
- критические секции (critical section);
- события (Events);
- мьютексы или взаимные исключения (Mutex);
- семафоры (Semaphore);
- таймер (Timer).
Кроме вышеперечисленных объектов, можно использовать и другие объекты, например потоки, изменения в файловой системе, консольный ввод и др.

Поведение потока при завершении его работы

Обычно при завершении своей работы поток просто освобождается. Однако иногда бывает необходимо, чтобы завершение работы и освобождение потока было согласовано с другими потоками. Например, вы можете ожидать какое-либо значение, возвращаемое одним потоком перед выполнением другого потока. Для реализации этого, вы не должны освобождать первый поток, пока второй не получит значение, возвращаемое первым. Для того чтобы управлять завершением работы потока, имеется свойство потока FreeOnTerminate: По умолчанию данное свойство установлено в true. При этом поток освобождается по завершении своей работы. Если же установить данное свойство в false, то вы можете сами явно завершить работу потока.
Кроме того, в Delphi имеется возможность прекращения выполнения одного потока из другого потока подачей команды о прекращении. Когда один поток пытается прекратить работу другого потока, он вызывает метод Terminate. В результате свойство Terminate потока будет установлено в true, что можно проверить во время выполнения метода Execute:
procedure TMyThread.Execute;
begin
while not Terminated do
{выполнять какие-либо задачи};
end;

Примечание
В случае аварийной ситуации можно завершить выполнение потока с помощью вызова функции Win32 API TerminateThread. Но, важно помнить, что к данной функции стоит обращаться только в том случае, когда никакие другие варианты завершения выполнения потока не приводят к успеху (например, бесконечный цикл). Опасность вызова данной функции состоит в том, что она ведет себя по-разному в различных операционных системах. В операционной системе Windows 9.x эта функция освобождает стек потока, а в Windows NT стек не освобождается, пока работает процесс, породивший данный поток. Вторая причина осторожного использования этой функции - возможность не освобождения ресурсов. То есть файлы, которые были открыты потоком, могут не закрыться, а также память, выделенная потоком, может не освободиться. Кроме того, при использовании в потоке, закрытом с помощью TerminateThread, динамически компонуемых библиотек (Dynamic Link Library, DLL), могут возникнуть проблемы с их закрытием.


Пример создания многопоточного приложения в Delphi

Теперь настало время для собственного многопоточного приложения.
Мы создадим простое приложение, которое состоит из трех потоков. Для начала запустим Delphi и выберем в главном меню Delphi пункт File/New Application (Файл/Новое приложение). После чего, разместим на главной форме приложения (Forml) поле для редактирования (Edit), индикатор хода выполнения работы (Progress Bar) и системный таймер (Timer). В результате должна получиться форма, аналогичная представленной на рис. 1.26.

Пример создания многопоточного приложения в Delphi

Рис. 1.26. Форма примера многопоточного приложения
Теперь добавим новый объект потока через пункт главного меню Delphi File/New/Thread Object (Файл/Новый/Объект потока). Введем имя для нового потомка класса TThread, например TMyThreadl. Delphi автоматически добавит модуль Unit2 в наше приложение. В описании объекта TMyThreadl добавим раздел public, в котором опишем глобальную переменную count. В результате должно получиться следующее:
type
TMyThreadl = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
public
count:integer;
end;
Далее запишем в метод Execute объекта TMyThreadl код, который должен выполняться в потоке. Пусть это будет код, который генерирует случайные числа и присваивает их глобальной переменной count. Для того чтобы генерация случайных чисел была бесконечной, зациклим ее с помощью так называемого "бесконечного цикла":
procedure TMyThreadl.Execute;
begin
while true do
begin
count:=random(maxint);
end;
end;
Теперь создадим второй объект потока, который должен заполнять индикатор хода работы (Progress Bar).
По аналогии с первым объектом потока, при помощи главного меню Delphi создадим объект потока с именем TMyThread2. Во вновь добавленном модуле units определим глобальную переменную prcount:
type
TMyThread2 = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
public
prcount:integer;
end;
После чего в процедуре Execute объекта TMyThread2 запишем следующий код, также зациклив его:
procedure TMyThread2.Execute;
begin
while true do
begin
prcount:=prcount+l;
if prcount>100 then prcount:=0;
end;
end;
Теперь сохраним наш проект и модули под теми именами, которые нам предложит Delphi (Projectl, Unitl, Unit2, Unit3).
Добавим в модуле uniti в описание класса формы в разделе private объекты потоков Threadl - потомок класса TMyThreadl и Thread2 - потомок класса TMyThread2:
type
TForml = class(TForm)
ProgressBarl: TProgressBar;
Editl: TEdit;
Timerl: TTimer;
procedure FormCreate(Sender: TObject);
private
Threadl:TMyThreadl;
Thread2:TMyThread2;
public
{ Public declarations }
end;
Далее, дважды щелкнем на любом свободном пространстве формы Form1. При этом откроется "заготовка" для метода создания формы FormCreate формы Formi. В обработчике FormCreate напишем следующий код:
procedure TForml.FormCreate(Sender: TObject);
begin
Threadl:=TMyThreadl.Create(False);
Threadl.priority:=tpLowest;
Thread2:=TMyThread2.Create(False);
Thread2.priority:=tpNormal;
end;
Здесь мы создаем объект потока с использованием конструктора Create и устанавливаем приоритеты потоков (tpLowest и tpNormal).

Примечание
Единственный параметр, который передается в методе Create класса TThread- CreateSuspended. Данный параметр может принимать логическое значение истины или лжи (true, false). Этот параметр показывает, создавать поток в приостановленном состоянии (true) или запускать его сразу же (false). То есть метод Execute будет вызван автоматически сразу, если параметр CreateSuspended равен false (как в нашем случае). Если же мы хотим запускать поток на исполнения в другое время, необходимо установить параметр CreateSuspended в true и воспользоваться методом Resume для вызова метода Execute на исполнение.

Запишем в-блоке uses модуля unit1 используемые модули unit2 и unit3:
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Unit2, Unit3, StdCtrls, ComCtrls;
Нам осталось отразить результаты вычислений, производимых потоками на форме. Для этого создадим обработчик события OnTimer, дважды щелкнув на объекте Timer1. Запишем в обработчик события OnTimer следующий код:
procedure TForml.TimerlTimer(Sender: TObject);
begin
editl.text:=inttostr(threadl.count);
progressbarl.position:=thread2.prcount;
end;
Все! Наше первое приложение, которое использует потоки, готово! Теперь, если запустить приложение с помощью главного меню Delphi: Run/Run (Запуск/Запуск), то мы можем видеть, как два созданных нами потока работают, причем с разной скоростью (в зависимости от приоритета).

Приоритеты потоков

Приоритеты указывают операционной системе, сколько процессорного времени выделяется для данного потока. Для критических задач можно установить наивысший приоритет, для менее значимых - более низкий приоритет.
Приоритет каждого потока складывается из двух составляющих: - класса приоритета - приоритета процесса, породившего поток; - относительного приоритета - приоритета самого потока.
Класс приоритета процесса может принимать одно из четырех значений: Idle, Normal, High и Realtime, которые выражаются числовыми значениями от 4 до 24. По умолчанию, любое приложение получает приоритет Normal. В табл. 1.2 представлены классы приоритетов процессов. Таблица 1.2. Классы приоритетов процессов
Значение класса

Приоритет

Числовое значение

Idle

Низший приоритет для выполнения фоновых задач

4

Normal

Стандартный приоритет, который имеют большинство приложений Windows

7-9

High

Приоритет высокого уровня, приложение получает больше процессорного времени, чем имеющее класс Normal

13

Reaitime

Наивысший уровень приоритета

24


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

Для определения текущего и установки требуемого класса приоритета используются функции GetPrioriryClass И SetPriorityClass соответственно.
Для установки высокого класса приоритета (High) для вашего приложения можно, например, использовать следующий код:
If not SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS) then ShowMessage ('Ошибка установки класса приоритета');

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

Для установки значения относительного приоритета у потоков имеется свойство Priority. Данное свойство может принимать семь значений. Используется как бы шкала значений приоритетов от самого низкого до наивысшего. Значения приоритетов потоков представлены в табл. 1.3.
Таблица 1.3. Приоритеты потоков
Значение относительного приоритета

Приоритет

Числовое значение

TpIdle

Данный поток выполняется, когда система не занята и не выполняются никакие другие потоки. Windows не будет прекращать работу других потоков для выполнения потока, имеющего приоритет

tpldle

-15

TpLowest

Низший приоритет выполнения. Данный поток занимает минимум процессорного времени

-2

ТрLower

Низкий приоритет. Данный поток занимает немного больше процессорного времени, чем имеющий приоритет tpLowest

-1

TpNormal

Нормальный приоритет. Все потоки по умолчанию имеют приоритет tpNormal

0

TpHigher

Высокий приоритет. Данный поток имеет приоритет выше нормального

1

TpHighest

Высший приоритет. Данный поток имеет приоритет выше, чем tpHigher

2

TpTimeCritical

Наивысший приоритет. Поток с данным приоритетом занимает максимум процессорного времени

15



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

Приведенный ниже пример показывает конструктор потока с самым низшим приоритетом, предназначенным для решения фоновых задач, которые не должны влиять на выполнение основных процессов:
constructor TMyThread.Create(CreateSuspended: Boolean);
{
inheritedCreate(CreateSuspended);
Priority := tpldle;
}

Семафоры

Иногда бывает необходимо ограничить число потоков, обращающихся к объекту ожидания. При этом число таких потоков может быть разным (не только один). Объект типа семафор может устанавливаться на доступ к нему определенного числа потоков. При этом все потоки, которые вновь обращаются к семафору, но, при этом, их количество уже превышает предельно допустимое количество, - приостанавливаются. При отключении присоединенных к семафору потоков "ждущие" потоки подключаются к нему.
Семафоры полезны при контролировании доступа к разделяемым ресурсам, которые могут поддерживать ограниченное число пользователей. Например, приложение может создать ограниченное число окон, отображаемых на экране. В данном случае можно использовать семафор, установленный на максимальное число окон, которые могут вывести приложение.
Потоки используют функцию CreateSemaphore для создания объекта типа семафор:
CreateSemaphore (IpSemaphoreAttributes, UnitialCount, IMaximumCount, IpName);
где:
- lpSemaphoreAttributes - определяет дескриптор безопасности для нового семафора, данный параметр игнорируется для Windows 9x. Если параметр равен NULL, то семафор получает дескриптор безопасности по умолчанию;
- lInitialCount - определяет начальное значение счетчика для семафора. Оно должно быть больше нуля и меньшим или равным значению lMaximumCount;
- lMaximumCount - определяет максимальное значение счетчика для семафора (максимальное число потоков). Оно должно быть больше нуля;
- lpName - определяет имя семафора. Имя может содержать в своем составе любые знаки и символы, за исключением символа обратной косой черты (\). Имя является чувствительным к строчным и прописным буквам. Если семафор с данным именем уже был определен, то будет осуществлен доступ к уже существующему семафору. Если значение IpName равно NULL, то семафор будет создан без имени. Если семафор будет иметь такое же имя, как и другой объект (например, мьютекс, взаимное исключение, или другой), то произойдет ошибка и при вызове функции GetLastError будет возвращено' значение ERROR_INVALID_HANDLE. Данная ошибка произойдет по простой причине, т. к. все эти объекты разделяют одно и то же пространство имен.

Примечание
Имя позволяет обращаться к семафору из потоков других процессов.


Синхронизация потоков

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

Данный объект ожидания является простейшим

Данный объект ожидания является простейшим для решения задачи синхронизации. Объект типа событие (TEvent) может принимать одно из двух состояний: активное или пассивное.
Когда объект ожидания находится в активном состоянии, его видят многие потоки одновременно. В результате такой объект можно использовать для управления работой сразу многих потоков.
Объект класса TEvent имеет среди других своих методов два метода: SetEvent, переводящий объект в активное состояние, и ResetEvent, переводящий объект в пассивное состояние.
Кроме того, этот объект содержит метод waitFor:
type TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError); function WaitFor(Timeout: DWORD): TWaitResult;
Данный метод имеет один параметр Timeout, задающий количество миллисекунд, в течение которых объект ожидает активизацию события. Этот метод возвращает значение wrSignaled в том случае, если активизация события произошла, и wrTimeout - в противном случае. Если в качестве параметра Timeout передать значение INFINITE, то ожидание активизации события будет бесконечно долгим.
Для создания объекта события можно воспользоваться функцией CreateEvent: CreateEvent (lpEventAttributes, bManualReset, bInitialState, lpName);
где:
- IpEventAttributes - определяет дескриптор безопасности для нового события. В Windows NT, если данный параметр равен NULL, то событие получает дескриптор безопасности, установленный по умолчанию. В Windows 9х данный параметр игнорируется;
- bManualReset - определяет ручной или автоматический сброс состояния события. Если значение данного параметра true, то вы должны использовать функцию ResetEvent для ручного перевода события в пассивное состояние. Если значение false, то Windows автоматически осуществит такой перевод;
- bInitialState - определяет начальное состояние события (активное или пассивное). Если значение параметра true, то состояние активное, иначе - пассивное;
- LpName - определяет имя/события для обращения к нему из других потоков. Имя может содержать любые символы, кроме обратной косой черты (\). Имя чувствительно к регистру букв. В случае, когда имя повторяет имя созданного события, происходит обращение к уже существующему событию. Если значение имени равно NULL, то событие создается без имени. Если событие имеет такое же имя, как семафор, мьютекс или другой объект, генерируется ошибка ERROR_INVALID__HANDLE.

Запуск и остановка потоков

Потоки могут быть запущены и остановлены сколько угодно раз в процессе их выполнения. Для временной остановки запущенного потока можно обратиться к методу потока suspend. Для продолжения выполнения приостановленного потока вызовите метод потока Resume. Вы можете использовать вложенные вызовы вышеперечисленных методов, т. к. метод Suspend увеличивает внутренний счетчик потока, a Resume уменьшает. Поток не будет выполняться до тех пор, пока счетчик не обратиться в ноль, т. е., если вы вызвали пять раз метод Suspend, а затем четыре раза Resume, вам понадобится еще один (пятый) вызов метода Resume для продолжения выполнения потока.

ActiveX в Delphi

Динамическая загрузка пакетов

Иногда требуется загружать необходимый пакет во время работы приложения. Для этой цели служит функция LoadPackage. Данная функция находится в модуле Delphi SysUtils. Описание функции имеет следующий вид: function LoadPackage(const Name: string): HMODULE;
В качестве передаваемого функции параметра служит имя файла пакета типа string. Вызов данной функции очень прост: LoadPackage('Имя файла пакета');
Для динамической выгрузки пакета применяется процедура unioadPackage. Эта процедура также содержится в модуле SysUtils. Ее описание имеет вид: procedure UnioadPackage(Module: HMODULE);
При вызове данной процедуры будьте осторожны, чтобы не уничтожить экземпляры классов, определенных в пакете.
Среда Delphi поставляется с уже установленными пакетами компонентов, которые поддерживают основные компоненты, необходимые для создания стандартных приложений Windows. Наибольшее число стандартных компонентов Delphi содержится в уже знакомом нам пакете VCL50.BPL. Но данный пакет не содержит многие компоненты, например компоненты для работы с базами данных, компоненты для Windows 3.1 и др. Эти компоненты находятся в других пакетах Delphi (например, компоненты, предназначенные для работы с базами данных, находятся в пакете VCLDB50.BPL). Однако, несмотря на это, для работы с базами данных, вам понадобятся оба пакета VCL50.BPL и VCLDB50.BPL. В приведенной ниже табл. 1.5 перечислены пакеты времени выполнения, поставляемые с Delphi (версии 5.0), а также модули (units), входящие в данные пакеты.
Таблица 1.5. Основные runtime-пакеты Delphi
Имя файла пакета

Модули, входящие в пакет

VCL50.BPL

Ax, Buttons, Classes, Clipbrd, Comctrls, Coraractrl, Commdlg, Comobj, Comstrs, Consts, Controls, Ddeml, Dialogs, Digs, Dsgnintf, Dsgnwnds, Editintf, Exptintf, Extctrls, Extdlgs, Fileintf, Forms, Graphics, Grids, Imm, IniFiles, Isapi, Isapi2, Istreams, Libhelp, Libintf, Lzexpand, Mapi, Mask, Math, Menu, Messages, Mmsystem, Nsapi, 01e2I, Oleconst, Olectnrs, Olectrls, Oledlg, Penwin, Printers, Proxies, Registry, Regstr, Richedit, Shellapi, Shlobj, Stdctrls, Stdvcl, Sysutils, Tlhelp32, Toolintf, Toolwin, Typinfo, Vclcom, Virtintf, Windows, Wininet, Winsock, Winspool, Winsvc

VCLX50.BPL

Checklst, Colorgrd, Ddeman, Filectrl, Mplayer, Outline, Tabnotbk, Tabs

VCLDB50.BPL

Bde, Bdeconst, Bdeprov, Db, Dbcgrids, Dbclient, Dbcommon, Dbconsts, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, Dsintf, Provider, Smintf

VCLDBX50.BPL

Dblookup, Report

DSS50.BPL

Mxarrays, Mxbutton, Mxcommon, Mxconsts, Mxdb, Mxdcube, Mxdssqry, Mxgraph, Mxgrid, Mxpivsrc, Mxqedcom, Mxqparse, Mxqryedt, Mxstore, Mxtables, Mxqvb

QRPT50.BPL

Qr2const, Qrabout, Qralias, Qrctrls, Qrdatasu, Qrexpbld, Qrextra, Qrprev, Qrprgres, Qrprntr, Qrqred32, Quickrpt

TEE50.BPL

Arrowcha, Bubblech, Chart, Ganttch, Series, Teeconst, Teefunci, Teengine, Teeprocs, Teeshape

TEEDB50.BPL

Dbchart, Qrtee

TEEUI50.BPL

Areaedit, Arrowedi, Axisincr, Axmaxmin, Baredit, Brushdlg, Bubbledi, Custedit, Dbeditch, Editchar, Flineedi, Ganttedi, leditcha, Pendlg, Pieedit, Shapeedi, Teeabout, Teegally, Teelisb, Teeprevi, Teexport

VCLSMP50.BPL

Sampreg, Smpconst

Рассмотрим теперь пакеты разработки, которые поставляются вместе с Delphi 5.0 (табл. 1.6). Напомним, что данные пакеты используются IDE Delphi для установки компонентов в палитру компонентов, задания свойств компонентов и многого другого.
Таблица 1.6. Основные design-time-компоненты Delphi
Имя файла пакета

Вкладки палитры компонентов

DCLSTD50.BPL

Standard, Additional, System, Win32, Dialogs

DCLTEE50.BPL

Additional (компонент TChart)

DCLDB50.BPL

Data Access, Data Controls

DCLMID50.BPL

Data Access (MIDAS)

DCL31W50.BPL

Win 3.1

DCLNET50.BPL, NMFAST50.BPL

Internet

DCLSMP50.BPL

Samples

DCLOCX50.BPL

ActiveX

DCLQRT50.BPL

Qreport

DCLDSS50.BPL

Decision Cube

IBSMP50.BPL

Samples (компонент IBEventAlerter)

DCLINT50.BPL

Мастер многоязыковой поддержки

RCEXPERT.BPL

Мастер ресурсов

DBWEBXPRT.BPL

Мастер Web


Все вышеперечисленные пакеты разработки вызывают при своей работе пакеты времени выполнения. Например, пакет DCLSTD50.BPL вызывает VCL50.BPL. Первый пакет содержит код, который позволяет делать доступ ными многие компоненты, входящие во второй пакет на палитре компонентов Delphi.
В дополнение ко всем рассмотренным выше пакетам, вы можете устанавливать в IDE пакеты собственного производства, а также пакеты, созданные другими разработчиками. Пакет Delphi DCLUSR50.BPL является стандартным контейнером для новых компонентов.

Для чего используются пакеты

Как мы уже говорили выше, пакеты разработки необходимы для упрощения задачи распределения и установки компонентов, созданных разработчиком. Программирование с помощью пакетов разработки, по определению, имеет некоторое преимущество по сравнению с обычным программированием. Главное преимущество - сокращение размера кода. Например, все ваши приложения, включая среду программирования Delphi, могут одновременно использовать стандартные компоненты Delphi, расположенные в одном и том же пакете. Так как приложения не содержат отдельные копии библиотек компонентов, то их размер становится намного меньше. Более того, время компиляции пакетов существенно меньше, т. к. для конкретного приложения компилируется тот код, который ему необходим.
Для того чтобы приложение использовало пакеты, нужно установить флажок Build with Runtime Packages (Строить с пакетами времени выполнения) в диалоговом окне Project Options (Настройки проекта) на странице Packages (Пакеты). Для открытия этого диалогового окна выберите пункт главного меню Delphi Project/Options (Проект/Настройки). При такой компиляции приложения оно значительно сократится в размерах (примерно в 8-10 раз), но вам придется при распространении приложения передавать и все используемые приложением пакеты (включая и такой большой пакет, как VCL50.DCP, который имеет объем почти 3 Мбайт).
Рассмотрим, когда нужно использовать пакеты, а когда стандартные динамически загружаемые библиотеки Windows:
- если вы хотите использовать самостоятельно написанные компоненты в среде Delphi - создавайте пакеты. Помните, что пакеты Delphi поддерживаются только приложениями, разработанными в Delphi или Borland C++ Builder;
- если же вы хотите использовать созданную вами библиотеку различными приложениями, созданными в разных средах программирования, вам необходима динамически загружаемая библиотека (*.DLL).
Кроме рассмотренного выше расширения файла пакета BPL, с файлами пакетов связаны следующие расширения (табл. 1.4):
Таблица 1.4. Типы файлов пакетов
Расширение файла

Содержание файла

DPK

Исходный файл пакета, содержащий список модулей, расположенных в пакете. Он создается при запуске редактора пакета (по своему назначению и функциональности он похож на файл проекта Delphi *.DPR)

DCP

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

DCU

Двоичный файл, содержащий текст модуля, находящийся в пакете. Для каждого модуля создается один файл *.DCU

BPL

Файл пакета, используемого во время работы приложения. Этот файл является аналогом динамически загружаемой библиотеки (*.DLL) Windows, которая содержит специфичные характеристики среды Delphi. Если это пакет времени выполнения, то вы должны передавать данный пакет пользователю вместе с файлом приложения. В случае, если это пакет разработки, он должен распространяться среди программистов, использующих его для написания приложений



Использование пакетов в приложениях

Для того чтобы приложение, которое использует пакеты, благополучно запустилось, нужно, чтобы имелись все необходимые приложению файлы ЕХЕ и BPL. Файлы BPL должны быть прописаны в путях приложения.

Примечание
При разработке коммерческих приложений обратите внимание на то, чтобы пользователи получили правильную версию файлов пакетов (*.BPL).

Для использования пакетов времени выполнения в вашем приложении нужно выполнить следующие шаги:
1. Загрузите или создайте новый проект в среде Delphi.
2. В главном меню Delphi выберите пункт Project/Options (Проект/Настройки).

Использование пакетов в приложениях

Рис. 1.27. Вкладка Packages диалогового окна Project Options
3. В открывшемся диалоговом окне выберите вкладку Packages (Пакеты) (рис. 1.27).
4. Установите флажок Build with runtime packages (Строить с пакетами времени выполнения). Затем в открывшееся поле введите один или несколько названий пакетов, которые вы хотите использовать в своем приложении. Вы можете воспользоваться также кнопкой Add (Добавить) для поиска необходимого пакета (рис. 1.28).

Использование пакетов в приложениях

Рис. 1.28. Окно добавления пакета в проект

Примечание
При изменении пути поиска (Search path) в диалоговом окне добавления пакета (рис. 1.28) вы измените глобальные настройки путей к библиотекам Delphi.

При ручном вводе имени пакета (в поле для редактирования без применения кнопки Add (Добавить) вам не потребуется вводить расширение пакета. Обратите внимание, что названия пакетов записываются через точку с запятой: VCL50;VCLX50;VCLSMPbO;VCLDB50;VCLAD050
Пакеты, перечисленные в поле для редактирования, при компиляции вашего приложения будут автоматически связаны с ним. Если в поле для ввода имен пакетов не будет ни одного названия пакета, ваше приложение будет откомпилировано как не использующее пакеты. В случае если один и тот же пакет будет записан в поле для редактирования несколько раз, все лишние записи будут проигнорированы.

Примечание
Вся информация об используемых приложением пакетах будет сохранена только для данного приложения. Если вы хотите, чтобы настройки сохранялись для других приложений, установите флажок Defaults (По умолчанию), находящийся в нижней части диалогового окна Project Options (Настройки проекта).

Очень важно помнить, что даже если вы используете в своем приложении пакеты, вы все равно должны прописывать имена модулей среды Delphi в блоке uses:
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,. Dialogs;

Хотя все из вышеперечисленных модулей входят в один пакет VCL50.BPL, если убрать их описание из блока uses, компилятор сгенерирует ошибку.

Компиляция пакета

Как откомпилировать пакет, мы с вами уже знаем, осталось только рассмотреть некоторые особенности.
Для начала рассмотрим директивы компилятора, которые могут включаться в файл, пакета (*.DPK) и файлы модулей пакета (табл. 1.7).
Таблица 1.7. Директивы компилятора пакетов
Директива

Применение

{$IMPLICITBUILD OFF}

Служит для предотвращения перекомпиляции пакета. Применяется в тех случаях, когда пакет не изменяется

{$G-} или { IMPORTEDDATA OFF}

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

{ $WEAKPACKAGEUNIT ON}

Когда в файле модуля встречается эта директива, компилятор опускает данный модуль из BPL-файла и создает локальную копию модуля тогда, когда это будет необходимо (при вызове модуля из приложения или пакета). Модуль, имеющий такую директиву, называется слабым пакетом (weakly packaged)

{$DENYPACKAGEUNIT ON}

То же, что И {IMPORTEDDATA OFF}

{$DESIGNONLY ON}

Компилирует пакет как пакет design time

{$RUNONLY ON}

Компилирует пакет как пакет runtime


Рассмотрим более подробно директиву { $WEAKPACKAGEUNIT }.
Слабый пакет применяется, когда пакет ссылается на отсутствующие библиотеки DLL. Данная директива применяется очень редко. Она позволяет обрабатывать специфические ситуации. Например, есть два компонента (в разных пакетах), которые обращаются к одному и тому же интерфейсному модулю DLL. Если приложение использует сразу оба компонента, то это приведет к загрузке двух экземпляров DLL, при этом могут возникнуть конфликты инициализации и использования глобальных переменных.

Примечание
Обратите внимание, что когда вы поставляете откомпилированную версию приложения вместе со всеми необходимыми пакетами, вам нужно учитывать, что должна совпадать версия пакета. То есть пакет, созданный в Delphi 5, не будет работать вместе с приложением, разработанным в Delphi 4.

Глава 3 Содержание Глава 5

Создание и редактирование пакетов

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

Создание и редактирование пакетов

Рис. 1.30. Щелкните на значке Package, чтобы создать новый пакет
Для создания пакета вам необходимо выполнить перечисленные ниже шаги:

Примечание
Для хорошего понимания перечисленных ниже шагов вам нужно ознакомиться со структурой пакета. Структура пакета рассматривается в следующем разделе.

1. Выберите в главном меню Delphi команду File/New (Файл/Новый). В появившемся диалоговом окне выберите значок Package (Пакет) (рис. 1.30). Сгенерированный пакет будет отображен в редакторе пакета (рис. 1.31).
Редактор пакета отображает два раздела: содержания (Contains) и требований (Requires).

Создание и редактирование пакетов

Рис. 1.31. Редактор пакета
2. Для добавления нового модуля в раздел Contains (Содержание) выберите этот раздел, затем щелкните на кнопке Add (Добавить) редактора пакета. Появится диалоговое окно Add (Добавление) (рис. 1.32).
На странице Add Unit (Добавление модуля) диалогового окна Add (Добавление) запишите в поле имени файла модуля имя файла *.PAS, который вы хотите включить в пакет, либо воспользуйтесь кнопкой Browse (Обзор) для непосредственного указания файла. В результате, выбранный файл модуля будет добавлен в раздел contains вашего пакета.
Можете повторить вышеперечисленные действия для добавления дополнительных модулей в пакет.

Создание и редактирование пакетов

Рис. 1.32. Диалоговое окно добавления нового модуля раздела Contains пакета
3. Для добавления пакетов в раздел Requires выберите этот раздел щелчком мыши, затем нажмите кнопку Add (Добавить) редактора пакета.
В появившемся диалоговом окне (рис. 1.33) в поле имени пакета (Package Name) впишите имя файла пакета (*.DCP), требуемого для работы вашего пакета. Вы также можете использовать кнопку Browse (Обзор) для непосредственного указания файла пакета. Повторяя данный шаг, вы можете добавить несколько файлов пакетов в раздел Requires вашего пакета.

Создание и редактирование пакетов

Рис. 1.33. Диалоговое окно добавления нового модуля раздела Requires пакета
4. Для выбора типа создаваемого пакета (runtime или design-time) щелкните на кнопке Options (Настройки) редактора пакета. Откроется диалоговое окно Project Options (Настройки проекта) (рис. 1,34).
5. Теперь выберите нужный тип пакета, выбором переключателя в разделе Usage Options (Параметры использования).
6. Последний шаг - компиляция созданного пакета. Нажмите кнопку Compile (Компилировать) редактора пакета.
Редактировать уже существующий файл пакета можно по-разному:
- выбрать пункт главного меню Delphi File/Open (Файл/Открыть) и найти файл пакета, который необходимо редактировать;
- выбрать пункт главного меню Delphi Component/Install Component (Компонент/Установить компонент), выделить нужный пакет в списке пакетов и нажать кнопку Edit (Правка);
- в открытом редакторе пакета выбрать в разделе Requires (Требования) нужный пакет, щелкнуть на нем правой кнопкой мыши для появления контекстного меню, где выбрать пункт Open (Открыть).

Создание и редактирование пакетов

Рис. 1.34. Диалоговое окно Project Options
Кроме того, вы можете редактировать файл пакета (*.DPK) точно так же, как и файл проекта (*.DPR) - вручную, как текстовый файл. Файл пакета содержит в себе три раздела:
- название пакета (в разделе Package);
- раздел требуемых файлов для данного пакета (раздел Requires);
- раздел, содержащий названия файлов модулей, входящих в данный пакет (раздел Contains).
Приведем в качестве примера содержимое файла пакета VCLDB50.DPK:
package VCLDB50;
requires VCL50;
contains Db, Dbcgrids, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, mycomponent in 'C:\components\mycomponent.pas';
end.

Структура пакета

Вспомним все, что мы уже знаем о пакетах, и нам нетрудно будет понять, из чего состоит пакет.
Пакет состоит из имени, разделов Requires и Contains.
package имя пакета;
requires файлы пакетов, необходимые для работы пакета;
contains файлы модулей, входящие в состав пакета;
end.
Имя пакета должно быть уникальным внутри проекта. Например, вы назвали пакет MyPack. При этом редактор пакета создает файл ресурсов с именем MyPack.dpk. При компиляции проекта образуются еще два файла с именами MyPack.blp и MyPack. dcp.
Файл MyPack.bpl является исполняемым файлом пакета (аналог *.DLL), a файл MyPack.dcp - двоичным файлом (аналог *.DCU).
Имя пакета нужно для того, чтобы вызывать его из приложения или вписывать его в раздел Requires другого пакета.
Раздел Requires, как нами уже говорилось ранее, содержит имена файлов пакетов, которые использует данный пакет. Следует заметить, что пакеты не могут содержать круговые ссылки, т. е. не должно возникать следующих ситуаций:
- пакет не должен содержать ссылку на самого себя;
-если пакет Packagel содержит в разделе Requires пакет Package2, то Package2 не должен содержать в своем разделе Requires пакет Packagel;
-если пакету Packagel Требуется пакет Package2, а пакету Package2 - Packages, то пакет Packages не должен содержать в разделе Requires пакет Packagel.
Раздел Contains включает в себя файлы модулей (*.PAS), входящих в проект.

Установка пакетов компонентов

Для того чтобы установить собственные пакеты или пакеты, созданные другими разработчиками, вам нужно выполнить следующие шаги:
1. В случае, когда вы устанавливаете новый пакет, скопируйте файлы пакета в директорию библиотеки Delphi. Убедитесь, что скопировали все необходимые файлы пакета (имеющие расширения BPL, DCP, DCU). Если вы устанавливаете файл пакета с расширением DCP (Delphi Package Collection), то вам не нужно копировать никакие другие файлы, т. к. DCP-файл содержит внутри себя все файлы пакета.
2. В главном меню Delphi выберите команду Component/Install Packages (Компонент/Установить пакеты), либо воспользуйтесь вкладкой Packages (Пакеты) в диалоговом окне свойств проекта, вызываемого командой главного меню Delphi Project/Options (Проект/Настройки) (рис. 1.29).

Установка пакетов компонентов

Рис. 1.29. Окно просмотра компонентов, входящих в пакет
3. В вызванном диалоговом окне вы можете видеть список всех доступных пакетов. Для установки любого пакета, имеющегося в списке, поставьте флажок напротив наименования пакета. Для удаления пакета из проекта уберите флажок напротив наименования пакета. Для просмотра компонентов, входящих в пакет, щелкните мышью на имени пакета и нажмите кнопку Components (Компоненты). На рис. 1.29 показан список компонентов, входящих в состав пакета Borland ADO DB Components.
4. Для добавления нового пакета в список нажмите кнопку Add (Добавить) и в открывшемся окне выберите файл пакета. При выборе файла пакета, имеющего расширение DPC, появляется диалоговое окно для обработки процесса извлечения файлов пакета из файла коллекции пакета (*.DPC).
5. Чтобы удалить пакет из списка, выберите пакет и нажмите кнопку Remove (Удалить).
Компоненты пакета устанавливаются на вкладки палитры компонентов Delphi.

ActiveX в Delphi

Динамические хранилища

Часто при создании приложений возникает задача, когда необходимо накапливать поступающие данные. В качестве примера можно привести задачу записи звука, прием данных по сети и т. п. В случае, когда объем передаваемых данных заранее известен - проблемы не возникает. В этом случае можно выделить блок памяти требуемого объема и заполнять его поступающими данными. Проблема может возникнуть, когда объем поступающих данных заранее неизвестен, и мы не знаем, какой объем памяти нам может понадобиться. Тогда используются так называемые динамические (или потоковые) хранилища. В Delphi в качестве таких хранилищ могут выступать динамические массивы, объект TMemoryStream и динамическое перераспределение памяти. Принцип работы всех перечисленных хранилищ достаточно прост: под запись данных выделяется блок памяти, затем, когда этот блок полностью заполняется данными, он изменяет свой размер на больший.
Нужно помнить, что такое изменение объема блока памяти внутри кучи достаточно неэффективно, особенно когда размер блока памяти достаточно большой. Сам процесс изменения объема блока памяти занимает большое количество системных ресурсов.

Экстренное завершение приложения

Для экстренного завершения работы приложения служит функция Win32 API FatalAppExit. Данная функция отображает окно сообщения о критической ошибке и, после закрытия этого окна пользователем, приложение завершает свою работу.

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

Глава 4 Содержание Глава 6

Кучи и менеджеры куч

Серьезные приложения интенсивно используют механизмы выделения и освобождения памяти. В коммерческих приложениях широко применяются такие элементы, как: динамические массивы, строки, объекты и многое другое. При этом, создание и удаление подобных элементов происходит довольно часто, а сами элементы имеют относительно небольшой размер.
Такая работа по выделению памяти и ее освобождению, по отношению к элементам небольшого размера, является неэффективной. Во-первых, снижается производительность, т. к. резервирование адресного пространства и выделение страниц памяти происходит на уровне ядра операционной системы. Во-вторых, теряются большие объемы памяти. Например, если приложение требует выделить 256 байт под строку, операционная система реально выделяет одну страницу памяти, объемом 4 или 8 килобайт (в зависимости от операционной системы).
Для решения проблемы выделения памяти небольшим элементам приложения была введена организация по принципу кучи (heap). Куча - это достаточно большой непрерывный участок памяти, из которого выделяются небольшие блоки. Для того чтобы куча могла функционировать, в операционную систему был включен так называемый менеджер кучи. Менеджер кучи - это специальный механизм, который следит за выделением и освобождением блоков памяти. Для каждого вновь созданного процесса Windows по умолчанию создает кучу. Все кучи, которые создаются операционной системой, являются потокобезопасными. Следовательно, у программиста есть возможность обращаться к одной куче из разных потоков одновременно. Для работы с менеджером кучи Win32 API можно пользоваться следующими функциями (табл. 1.10).
Таблица 1.10. Функции Win32 API для работы с кучей
Функция

Назначение

HeapCreate

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

HeapAlloc

Выделяет блок неперемещаемой памяти в куче

HeapReAlloc

Служит для изменения размера блока памяти, выделенного функцией HeapAlloc

HeapFree

Освобождает блок памяти, выделенной функцией HeapAlloc

HeapDestroy

Уничтожает кучу, созданную с помощью функции

HeapCreate

VirtualAlloc

Резервирует или размещает страницы в виртуальной памяти процесса

VirtualFree

Освобождает страницы в виртуальной памяти процесса

VirtualLock

Защищает диапазон адресов от переноса в страницу памяти

VirtualUnlock

Снимает защиту, установленную функцией VirtualLock

VirtualQuery

Выдает информацию о диапазоне страниц в виртуальной памяти процесса, вызвавшего данную функцию

VirtualQusryEx

То же, что И VirtualQuery, только для определенного процесса

VirtualProtect

Изменяет права доступа к определенному диапазону памяти в адресном пространстве процесса, вызвавшего данную функцию

VirtualProtectEx

То же, что и VirtualProtect, только для определенного процесса


Кроме перечисленных в табл. 1.10, существуют еще функции LocalAlloc (GlobalAlloc) и LocalFree (GlobalFree). Вы можете использовать любое на-писание функций, т. к. в Win32 нет разделения на глобальные и локальные кучи. Данные функции применяются, в основном, при работе с буфером обмена Windows. Эти функции являются устаревшими и не рекомендуются к использованию.
Кроме стандартного менеджера кучи, который предлагает Microsoft, создателями Delphi был предложен свой менеджер кучи, который поставляется вместе с Delphi. Менеджер кучи Delphi резервирует блоки виртуальной памяти размером 1 Мбайт. Размер выделяемых Delphi блоков реальной памяти - 16 Кбайт. При работе с менеджером куч Delphi можно использовать следующие функции:
- New, Dispose - применяются для выделения и освобождения памяти. Используются для динамической работы с элементами приложения;
- GetMem, FreeMem - также применяются для выделения и освобождения памяти. Используются для динамической работы с небольшими двоичными блоками памяти (блоки, буферы и т. п.).
Кроме вышеперечисленных менеджеров куч, программисту предоставлена возможность создания собственного менеджера. .

Многозадачность

Многозадачность (multitasking) - свойство операционной системы выполнять одновременно несколько приложений.

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

В ранних, 16-разрядных операционных системах поддерживалась так называемая кооперативная многозадачность (cooperative multitasking). Это такой вид многозадачности, когда приложение в процессе своего выполнения само "решает" передавать управление операционной системе или продолжать занимать процессорное время. При этом другие приложения, запущенные вместе с первым, просто не выполняют никаких действий. Данный вид многозадачности приводил к "зависаниям" операционной системы вместе со всеми запущенными приложениями, если "висло" приложение, захватившее ресурсы процессора.
На смену кооперативной многозадачности пришла вытесняющая многозадачность (preemptive multitasking). Вытесняющая многозадачность появилась лишь в 32-разрядных операционных системах. Данный вид многозадачности подразумевает, что управление всеми приложениями ведет операционная система. Она выделяет каждому приложению определенный квант времени процессора в зависимости от приоритета приложения (см. главу 3).

Объекты GDI и User

В Windows 3.1 не было объектов ядра. Доступ ко всем объектам операционной системы осуществлялся с помощью дескрипторов. Объекты операционной системы делились на две группы: объекты, находящиеся в локальной памяти модулей GDI и User, и объекты, находящиеся в глобальной памяти.
Интерфейс графического устройства (Graphical Device Interface, GDI) - это часть Windows, которая управляет шрифтами, средствами печати и другими графическими системами Windows.
Когда приложение выводит что-либо на экран, оно использует службы, представляемые GDI.
Объекты GDI- это палитры, изображения, шрифты, и т. д., т.е. то, чем управляет GDI.
Средства пользовательского интерфейса (User) - это часть Windows, отвечающая за все окна, которые создаются приложениями.
Объекты User - это, в первую очередь, окна, меню.
В 16-разрядной версии Windows имелась непосредственная связь между объектом и его дескриптором. Таким образом, в данной версии Windows существовала таблица, содержащая указатели на все объекты операционной системы. Эта таблица была доступна любому приложению или динамически компонуемой библиотеке Windows. Она называлась таблицей локальных дескрипторов (Local Descriptor Table). В результате, любое приложение (или DLL) могло обращаться к объекту, используемому другим приложением.
В 32-разрядной операционной системе объекты хранятся в собственных адресных пространствах процессов, и для каждого процесса существует своя собственная таблица дескрипторов объектов. Таким образом, каждый процесс теперь работает с собственными дескрипторами объектов.
Любые дескрипторы объектов GDI или User управляются специальными подсистемами Win32 API. Для GDI - это GDI.EXE, для User - USER.EXE. Данные подсистемы выполняют создание, освобождение и проверку корректности работы дескрипторов объектов.

Объекты и процессы ядра Windows

Сразу оговоримся, что все сказанное в этой главе относится к следующим версиям Windows: Windows 95, 98, 2000 и Windows NT, т. к. только в данных версиях была введена поддержка 32-разрядных приложений.

Примечание.
Для среды Windows 3.1 Microsoft специально разработала пакет Win32s, позволяющий с некоторыми ограничениями использовать поддержку приложений Win32.

Ядро Windows (Windows kernel) - это часть операционной системы, которая обеспечивает поддержку низкоуровневых функций, необходимых для выполнения приложений. Например, всякий раз, когда приложению нужна дополнительная память, оно обращается к ядру Windows.
Между всеми вышеперечисленными системами существуют различия в поддержке 32-разрядных приложений. В табл. 1.8 перечислены некоторые отличия, существующие между тремя операционными системами.
Таблица 1.8. Различия операционных систем при поддержке Win32 API
Характеристика

Windows NT

Windows 95

Windows 3.1 с поддержкой Win32

32-битная система координат

Есть

Нет

Нет

Асинхронный файловый ввод/вывод

Есть

Нет

Нет

Асинхронная модель ввода информации

Есть

Есть

Нет

Мультимедиа API

Есть

Есть

На уровне Windows 3.1

Поддержка многопроцессорных материнских плат

Есть

Нет

Нет

Динамический обмен данными (DDE) по сети

Есть

Есть

Нет

Поддержка процессоров других фирм-производителей (не Intel)

Есть

Нет

Нет

Вытесняющая многозадачность

Есть

Есть

Нет

Безопасность (сертификат С2)

Есть

Нет

Нет

Разделяемое адресное пространство

Есть

Есть

Нет

Поддержка TAPI (Telephone API)

Есть

Есть

Нет

Потоки

Есть

Есть

Нет

Системные ресурсы для объектов User и GDI

Практически не ограничены

Расширенные

Ограниченные


Итак, Win32 API (Application Programming Interface) - это интерфейс разработки 32-разрядных приложений Windows.

Объекты ядра Windows

Рассмотрим объекты, с которыми работает операционная система Windows.
Сразу обратим ваше внимание на то, что объекты Win32 и объекты Delphi - абсолютно разные объекты. В Win32 объекты делятся на объекты ядра (kernel) и объекты GDI и User.

Объекты ядра

Объекты ядра (kernel objects) - это процессы, потоки, события, семафоры, мьютексы и т. д., т. е. все то, с чем работает ядро Windows.
При создании объекта он существует в адресном пространстве процесса. При этом дескриптор объекта доступен породившему его процессу. Данный дескриптор не может быть использован другими приложениями для доступа к объекту разных процессов. Но эта проблема разрешима: с помощью функций Win32 API процесс имеет возможность получения собственного дескриптора (отличного от дескриптора процесса, породившего объект) для уже существующего объекта.
Например, первый процесс создает именованный или неименованный мьютекс и возвращает его дескриптор с помощью команды createMutex. Для того чтобы данный мьютекс мог использоваться другим процессом, необходимо воспользоваться функцией openMutex, которая возвращает дескриптор уже существующего мьютекса.
При обращении к объектам ядра Windows использует счетчик обращений к объекту. При создании или использовании объекта ядра приложениями счетчик увеличивается. При прекращении использования объекта ядра приложением счетчик уменьшается. Наконец, при обнулении счетчика объект ядра уничтожается.

Обработка ошибок с помощью функции GetLastError

При неудачном завершении своего выполнения функция возвращает значение false. Для того чтобы получить код происшедшей ошибки, можно воспользоваться функцией Win32 API GetLastError. Данная функция не содержит параметров.

Примечание
Не во всех случаях при возвращении функцией значения false, GetLastError возвращает код происшедшей ошибки. Поэтому, в некоторых случаях, вызов функции GetLastError не приведет к нужному результату.

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

Примечание
Коды ошибок можно посмотреть в справочной системе Win32 Programmer's Reference, поставляемой вместе с Delphi.

Рассмотрим пример использования функции GetLastError:
If not CreateProcess (CommandLine, nil, nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, Startuplnfo, Processlnfo) then
Raise Exception.Create ('Ошибка создания процесса: ' +
IntToStr (GetLastError));
Данный код пытается создать процесс. При возникновении ошибки будет сгенерировано исключение, которое выведет код ошибки, полученный с помощью функции GetLastError.

Обработка ошибок с помощью функции SetErrorMode

Некоторые ошибки, такие как, например, "Устройство не готово" (при записи информации на дискету, которая защищена от записи), вызывают окна сообщений Windows, для информирования пользователя о случившейся ошибке. Приложение может обрабатывать такие ошибки, с помощью использования функции SetErrorMode:
SetErrorMode (uMode);
В данную функцию можно передавать любое из перечисленных в табл. 1.11 значение параметра uMode.
Таблица 1.11. Значения параметра uMode функции SetErrorMode
Значение параметра

Выполняемое действие

SEM_FAILCRITICALERRORS

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

SEM_NOALIGNMENTFAULTEXCEPT

Данное значение не используется для процессоров семейства х86

SEM_NOGPFAULTERRORBOX

Данное значение применяется при отладке приложений. При установке этого значения операционная система не отображает окно сообщения об ошибке

SEM_NOOPENFILEERRORBOX

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



Многие из функций Win32 API

Многие из функций Win32 API возвращают в качестве результата своего выполнения значение true или false. По данному значению мы можем узнать, успешно ли выполнилась функция.

Организация виртуальной памяти в Windows

Как нам уже известно, Win32 - это 32-разрядная операционная система. Таким образом, любое приложение, запущенное в Win32, может захватывать адресное пространство в размере 4 Гбайта. Каждое приложение (процесс операционной системы) обладает своим индивидуальным адресным пространством, которое не пересекается с адресными пространствами других приложений.
Процесс выделения памяти в Windows состоит из двух последовательных этапов:
- резервирования участка памяти (виртуального адресного пространства), необходимого размера для размещения приложения и его данных. На этом этапе, физически, операционная система не выделяет оперативную память. То есть, фактически, вы можете дать команду операционной системе зарезервировать 300 Мбайт адресного пространства, и при этом не займете практически никаких системных ресурсов. Вы можете непосредственно указать операционной системе адреса, которые резервируете, или предоставить это дело операционной системе;

Примечание
Адресное пространство в Win32 резервируется блоками по 64 Кбайта. Поэтому, несмотря на ваши пожелания, при резервировании адресного пространства операционная система реально зарезервирует первый (базовый) участок адресного пространства объемом 64 Кбайта. Данное разбиение адресного пространства на блоки служит для ускорения работы ядра операционной системы.

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

Примечание
Минимальный блок памяти, с которым работает операционная система, называется страницей памяти. Размер страницы памяти различен в разных операционных системах. Например, в Windows NT - он равен 8 Кбайт, а в Windows 95/98 - 4 Кбайта.

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

Процессы и потоки

Процесс - это выполняющееся приложение Windows. Так как Windows - многозадачная операционная система, то в ней может работать сразу несколько процессов. Каждый процесс получает свое адресное пространство (размером до 4-х гигабайт). В этом пространстве хранится код приложения, его данные, а также все подключаемые библиотеки (DLL).
Сами процессы ничего не выполняют. Каждый процесс состоит из потоков (threads), которые выполняют код процесса (подробнее о потоках и об их создании см. главу 3). Любой процесс состоит как минимум из одного потока, который называется первичным или главным потоком (primary thread). Процесс может состоять из нескольких потоков, только один из которых будет главным.
Поток - это объект операционной системы, который представляет собой часть кода, находящегося внутри некоторого процесса. .
При создании процесса операционная система создает его главный поток, который может генерировать дополнительные потоки. При этом каждому потоку процесса Windows выделяет свои кванты времени процессора, в зависимости от приоритета потока.
Для работы с процессами Win32 API имеет встроенные функции, перечисленные в табл. 1.9.
Таблица 1.9. Функции Win32 API для работы с процессами
Функция Win32 АРI

Предназначение

CreateProcess

Создает новый процесс и его главный поток. Используется вместо функции Windows 3.1 winExec. При помощи данной функции можно запускать приложения

ExitProcess

Завершение выполнения процесса и всех его потоков

GetCurrentProcess

Возвращает псевдодескриптор текущего процесса. Настоящий дескриптор текущего процесса можно получить С ПОМОЩЬЮ функции DuplicateHandle

DuplicateHandle

Функция, предназначенная для создания копии объекта ядра

GetCurrentProcess ID

Возвращает идентификатор текущего процесса. Данный идентификатор уникален для каждого процесса операционной системы

GetExitCodeProeess

Получение статуса окончания процесса

GetPriorityClass

Возвращает класс приоритета для конкретного процесса

GetStartupInfo

Возвращает содержимое структуры TStartupinfo,

которая создается во время создания процесса

OpenProcess

Возвращает дескриптор процесса по его идентификатору

SetPriorityClass

Устанавливает класс приоритета для конкретного процесса

TerminateProcess

Прекращение выполнения процесса и всех его потоков

WaitForInput Idle

Перевод процесса в режим ожидания ввода



В данном разделе мы рассмотрим

В данном разделе мы рассмотрим все, чего еще не касались ранее о распределении памяти в Win32.

ActiveX в Delphi

Иерархия визуальных компонентов

Рассмотрим иерархию классов Delphi, частью которой является и VCL (рис. 2.12).

Иерархия визуальных компонентов

Рис. 2.12. Иерархия классов Delphi
Как мы уже знаем, все классы, и компоненты в том числе, являются прямыми или косвенными потомками класса TObject. Класс TObject инкапсулирует небольшой набор обычных для всех классов методов:
- способность создания, поддержки и уничтожения экземпляра класса с выделением, инициализацией и освобождением оперативной памяти для данного экземпляра класса;
- предоставление информации об экземпляре класса и информации о типе времени выполнения (RTTI);
- поддержка обработки сообщений;
- поддержка интерфейсов, осуществляющих экземпляр класса.
Следующий базовый класс, являющийся прямым потомком TObject - класс TPersistent. Данный класс является предком для всех классов, предназначенных для работы с потоками. Класс TPersistent наследует все свойства и методы своего предка - класса TObject, и может, кроме того, считывать данные из потока и записывать их в поток. Класс TPersistent обеспечивает:
- определения процедур для загрузки и помещения данных в поток;
- назначение свойствам экземпляра класса конкретных значений;
- средства для передачи содержимого одного экземпляра класса другому.
- отомком класса TPersistent является класс TComponent - базовый класс всех компонентов визуальной библиотеки компонентов (VCL) Delphi. Наследники класса Tcomponent, как и сам класс имеют следующие возможности:
- способность отображаться на палитре компонентов Delphi, а также способность размещения на форме;
- способность быть обладателем иных компонентов и управлять другими компонентами;
- способность быть преобразованным в элемент управления ActiveX или объект СОМ с помощью мастера (Wizard).
Следующий класс - потомок вышеописанного класса TComponent - класс Tcontrol. Класс TControl является базовым классом для всех визуальных компонентов, т. е. потомки этого класса будут видны и доступны пользователю во время выполнения приложения. Все классы, являющиеся потомками TControl, имеют свойства, методы и события, которые определяют особенности данного класса, например положение компонента на экране, размер и т. д.
Нам осталось рассмотреть еще два класса, которые являются потомками класса TControl.
Первый из этих классов - класс TGraphicControl. Данный класс предназначен для создания визуальных компонентов, которые не являются окнами, т. е. не могут содержать в себе других компонентов и получать фокус ввода. Главное предназначение данного класса - способность отображать графику или текст на компоненте. Примерами потомков класса TGraphicControl могут выступать такие компоненты, Как TBevel, TImage, TSpeedButton И Др.
Класс TGraphicControl передает своим потомкам свойство canvas, позволяющее получать доступ к поверхности компонента, на которой можно рисовать или писать. Кроме того, класс TGraphicControl передает своим потомкам метод Paint. Так как графический компонент - потомок TGraphicControl не нуждается в идентификаторе окна Windows, данный компонент требует немного ресурсов, и использование графического компонента занимает меньше времени, чем оконного компонента Twincontrol.
Второй из этих классов - класс Twincontroi. Данный класс является базовым классом для всех оконных визуальных компонентов. Оконный визуальный компонент - это компонент, который:
- может получать фокус ввода во время выполнения приложения;
- может содержать в себе другие компоненты. Компонент, который содержит в себе другие компоненты, называется родительским (parent). Компонент, содержащийся в другом компоненте, называется дочерним (child);
- имеет идентификатор окна Windows (handle). Каждый оконный компонент обрабатывается непосредственно операционной системой, поэтому ему назначается свой уникальный идентификатор.
Примерами оконных компонентов являются TButtonControl, TCustomEdit и др.

Компоненты

Компоненты Delphi являются частью классовой иерархии Delphi, которая называется библиотекой визуальных компонентов (Visual Component Library, VCL). Библиотека визуальных компонентов Delphi, несмотря на название, содержит в своем составе как визуальные (видимые во время выполнения приложения), так и невизуальные (видимые только на этапе проектирования) компоненты. К визуальным компонентам относятся TButton, TEdit, TLabel и др. К невизуальным относятся такие компоненты, как TTimer, TTable, TQuery И др.
Компонент - это небольшая часть приложения, обеспечивающая создание пользовательского интерфейса. Компоненты можно представить как небольшие "кирпичики", из которых строится "дом" - приложение.
Компоненты могут находиться на панели компонентов. Разработчик может брать компоненты из панели компонентов и располагать их на форме.
Компоненты могут сильно отличаться по степени своей сложности. Можно использовать в своем приложении как простые компоненты (как, например, TLabel, TEdit), так и достаточно сложные (скажем, компонент, объединяющий в себе возможности текстового процессора).
Для того чтобы успешно работать с компонентами, нужно понимать основные типы компонентов Delphi, а также представлять себе их внутреннее строение и иерархию компонентов. Все это мы опишем далее в этой части книги.

Методы

Методы - это процедуры или функции, принадлежащие объекту. Методы определяют поведение объекта. Для вызова метода объекта сначала записывается имя объекта, с которым ассоциирован данный метод, затем, через точку, название метода. Например:
Buttonl.Click
Вызывает метод нажатия (click) кнопки (Buttonl).
Для создания метода его нужно сначала объявить внутри описания класса или компонента, содержащего данный метод. Например:
type TMyComponent = class(TObject)
procedure DoSomething;
end;
Здесь, внутри описания нового компонента, объявляем метод DoSomething с помощью служебного слова procedure. После того как мы объявили новый метод, мы должны создать тело данного метода. Эта процедура может находиться где угодно внутри модуля, в котором был описан компонент. Например:
procedure TMyComponent.DoSomething; begin
// Здесь размещаем команды и операторы, которые должны выполняться
// при вызове метода DoSomething на выполнение
end;
Заметим, что при создании процедуры DoSomething мы должны указывать его полное имя вместе с указанием имени компонента или класса (procedure TMyComponent.DoSomething;).
Рассмотрим основные методы, которые имеются в среде Delphi.
Метод Add предназначен для добавления нового элемента в список типа TList, TstringList или Tstrings. Если список не отсортирован, то новый элемент добавляется в самый конец списка. Если список отсортирован, то новый элемент добавляется в позицию списка, определяемую сортировкой. Вызов этого метода увеличивает значение свойства Count для данного списка. При выполнении подобного метода может возникнуть исключение EListError в случае, когда список отсортирован, добавляемая строка уже присутствует в списке и для данного списка свойство Duplicates установлено В dupError..
Метод Аrc предназначен для рисования дуги окружности или эллипса. В качестве параметров метода передаются координаты четырех точек. Первые две точки (X1, YI) и (X2, Y2) определяют прямоугольник, описывающий эллипс. Следующая точка (X3, YЗ) задает начальную точку дуги, которая находится на пересечении прямой, проходящей через центр окружности или эллипса и точки (X3, YЗ). Точка (X4, Y4) фиксирует конечную точку дуги, которая находится на пересечении прямой, проходящей через центр окружности или дуги и точки (X4, Y4). Дуга рисуется против часовой стрелки от начальной до конечной точки. Для наглядности, приведем рис. 2.3.

Методы

Рис. 2.3. Координаты точек, задаваемых при вызове метода Arc
Для рис. 2.3 можно привести такой пример вызова метода Arc: Imagel.Canvas.Arc (0,0, 200,100, 200,10, 0,0);

Примечание
Если вы создаете приложение, предназначенное для работы в Windows 95, обратите внимание на суммы Х1+Х2, Y1+Y2 и X1+X2+Y1+Y2. Они не должны превышать числа 32 768.

Метод Assign применяется для копирования данных одного объекта в другой. Данный метод имеет отличие от простого присваивания: объект1 := объект2. Разница заключается в том, что при присваивании указатель на <объект-назначение> начинает указывать на <объект-источник>, а метод Assign создает новую копию объекта. После применения Assign имеется два объекта с одинаковыми данными. Если объекты разного типа, то при вызове D.Assign(S) тип D должен "знать", как скопировать в него тип s (тип s может ничего не знать о преобразовании типов). Если метод Assign не может осуществить преобразование типов, то он вызывает защищенный метод AssignTo, объявленный в классе TPersistent и перегруженный в классах, производных от него. Вызов имеет вид s.AssignTo(D). Если и метод AssignTo не может осуществить преобразование или если он не перегружен, то вызывается AssignTo класса TPersistent и генерируется исключение. Метод Assign можно применять в разных ситуациях. Хорошей иллюстрацией использования данного метода может служить копирование изображения из буфера обмена в графический компонент Timage:
Imagel.Picture.Assign (Clipboard);
и, наоборот, из компонента в буфер обмена
Clipboard.Assign (Imagel.Picture);

Примечание
Для работы с буфером обмена Windows необходимо в блок uses модуля, в котором идет работа с Clipboard, добавить ClipBrd.

Метод BeginDrag вызывается, когда начинается процесс перетаскивания компонента. Данный метод применяется только в случае, когда свойство DragMode компонента имеет значение dmManual. В случае, когда свойство имеет значение dmAutomatic, перетаскивание компонента осуществляется автоматически. Вызов метода BeginDrag обычно вставляют в обработчик события onMouseDown. Параметр immediate метода BeginDrag показывает, сразу ли после нажатия кнопки мыши указатель курсора сменит вид на тот, который определен в свойстве DragCursor, и сразу ли начнется процесс перетаскивания. В случае, когда параметр immediate имеет значение false, перетаскивание начинается только после того, как пользователь щелкнет на компоненте и сместит указатель мыши на расстояние в пять пикселов. Это удобно для того, чтобы обрабатывать щелчок на компоненте, не начиная его немедленного перетаскивания.
Метод BringToFront применяется для переноса компонента наверх в так называемой Z-последователъности. Z-последовательность определяет порядок компонентов в свойстве controls родительского оконного компонента (см. раздел "Иерархия визуальных компонентов" этой главы). Тот компонент, который расположен в Z-последовательности выше других, в случае частичного или полного перекрытия компонентов, будет виден. Таким образом, вызов метода BringToFront позволит переместить "наверх" компонент, скрытый под другими компонентами. Данный метод можно применять как к неоконным, так и к оконным компонентам. Однако все неоконные компоненты располагаются в Z-последовательности ниже оконных, поэтому в случае, когда неоконный компонент перекрыт оконным, например кнопка перекрыта компонентом типа TMето, вызов метода - BringToFront для кнопки ничего не даст.
Метод BrushCopy вызывается для копирования части изображения битовой матрицы на область вывода (канву) и замены указанного цвета в изображении на значение, установленное для кисти канвы. Данный метод сохранился от предыдущих версий Delphi и применяется только для совместимости с ними.
Метод CanFocus используется для определения, может ли данный компонент получать фокус. Этот метод возвращает значение true, если у компонента и всех его родителей свойства visible и Enabled имеют значения true.
Метод changeScale применяется для изменения масштаба компонента и всех его дочерних элементов. При вызове этого метода масштабируются такие свойства компонента, как тор, Left, width, Height. Метод ChangeScale имеет два параметра, определяющие множитель и делитель масштаба: м и о. Приведем пример использования данного метода. Допустим, мы хотим увеличить размер формы Forml в два раза, для этого зададим множитель масштаба равный двум, а делитель - единице:
Forml.ChangeScale (2,1);
При выполнении данной строки размеры формы Forml увеличатся в два раза.
Для уменьшения размеров, например, в три раза, нужно задать значение м, равное единице, а значение D, равное трем:
Forml.ChangeScale (1,3);
Метод chord применяется для рисования замкнутой фигуры, границами которой являются дуга окружности или эллипса и хорда. Параметры данного метода аналогичны параметрам метода Arc. Результатом работы метода chord могут стать фигуры, изображенные на рис. 2.4.

Методы

Рис. 2.4. Фигуры, получаемые после вызова метода Chord
Метод className применяется для определения имени типа компонента.
Метод clear предназначен для удаления всех элементов, входящих в список, или для удаления всего текста, входящего в компонент.
Примеры:
ListBoxl.Clear; Memol.Clear;
Метод Clear можно применять и к объекту Clipboard. вызов метода Clear для буфера обмена удалит все содержимое буфера.
Метод click служит для вызова обработчика события Onciick для данного компонента. Применяется для имитации нажатия левой кнопки мыши над объектом. Например, для вызова обработчика события Onciick кнопки Buttoni можно проделать следующее:
Button1.Click;
Метод clientloScreen применяется для преобразования координат клиентской области компонента в координаты экрана. Началом координат клиентской области является левый верхний угол клиентской области компонента. Началом координат экрана - левый верхний угол экрана. Для обратного преобразования координат можно использовать метод ScreenTodient.
Метод containsControi определяет, каким наследником (прямым или косвенным) является данный компонент по отношению к какому-либо оконному компоненту. Если этот компонент прямой наследник, то метод возвращает true, иначе - false.
Метод controlAtpos предназначен для оконных компонентов. Он применяется для того, чтобы определить, какой дочерний компонент находится в позиции с координатами, задаваемыми параметром POS. В случае, если в заданной позиции нет ни одного дочернего компонента, метод ControiAtpos возвращает в качестве результата значение nil. Второй параметр AllowDisabled определяет, нужно ли учитывать при вызове метода те компоненты, которые отключены ( которые Disabled = true).
Метод copyRect применяется для копирования прямоугольной части изображения с одной канвы на другую. Копирование осуществляется в том режиме, который определен свойством copyMode.
Метод Delete применяется по отношению к таким компонентам, как TList, TStringList, TStrings и TMenuitem. Данный метод вызывается при необходи-мости удаления какого-либо элемента из списка. В качестве параметра этого метода передается число, определяющее индекс удаляемого элемента. Нумерация элементов начинается с нуля. При удалении пункта меню, содержащего подменю, удаляется и данный пункт, и его подменю.

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

Метод Destroy применяется для вызова деструктора объекта. То есть вызов данного метода уничтожает объект и освобождает занимаемую память.

Примечание
Не рекомендуется вызывать непосредственно метод Destroy для уничтожения объекта. Для этих целей лучше использовать метод Free, он проверяет, не была ли уже раньше освобождена занимаемая объектом память, после чего вызовет метод Destroy. Более того, метод Free генерирует меньший код. Для уничтожения форм используйте метод Release.

Метод DisabieAiign применяется для временного запрета выравнивания дочерних компонентов оконного компонента. Обратное действие можно получить, ВОСПОЛЬЗОВаВШИСЬ МеТОДОМ EnableAlign.
Метод Dormant служит для создания битовой матрицы в оперативной памяти для освобождения дескриптора матрицы. Применение данного метода позволяет сократить расходы ресурсов GDI, которые используются приложением. Приведем пример (листинг 2.1).

Листинг 2.1
// Загрузка картинки в Bitmapl
Bitmapl.LoadFromFile ('mypicture.bmp');
// Копирование в Bitmap2 из Bitmapl
Bitmap2.Assign (Bitmapl);
// Применение метода Dormant и освобождение ресурсов GDI
Bitmap2.Dormant;

Метод Draw применяется для рисования изображения, хранящегося в объекте, который определен параметром Graphic, в координаты, задаваемые параметрами х и У. Изображение может быть либо битовой матрицей, либо пиктограммой, либо метафайлом. Например:
Imagel.Canvas.Draw (10, 10, Image2.Picture.Bitmap);
Таким образом, в координаты 10, 10 канвы картинки image1 будет занесен рисунок из канвы картинки Image2.
Метод DrawFocusRect предназначен для рисования прямоугольника с помощью булевой операции XOR. То есть повторное рисование такого же прямоугольника на том же месте удалит этот прямоугольник. Пример:
Imagel.Canvas.DrawFocusRect (Rect (0,0,30,30);
Метод Ellipse служит для рисования окружности или эллипса. Параметры - точки (X1, YI) и (X2, Y2) определяют прямоугольник, в который вписан эллипс.
Метод Error вызывается при необходимости генерации исключения при работе с объектом типа TList. Вызов данного метода дает лучший результат по сравнению с командой Raise. Примером вызова этого метода может служить следующая строка:
List.Error ("Ошибка в элементе %и списка List', I);
Выполнение данной строки вызовет сообщение об ошибке в какой-либо строке списка.
Метод Exchange предназначен для обмена местами двух элементов списка. Позиции этих двух элементов задаются параметрами index1 и index2.

Примечание
Не используйте метод Exchange для отсортированных списков - это может нарушить упорядоченность списков.

Метод Expand служит для увеличения емкости списка типа TList. Вызов данного метода приводит к выделению дополнительной памяти для быстрого добавления новых элементов списка. В случае когда при вызове данного метода список не заполнен, то его емкость не изменяется, иначе - увеличивается.
Метод FiliRect применяется для заполнения указанного прямоугольника канвы цветом, определенным значением свойства Brush. Например, приведенный ниже код заполняет всю область канвы компонента image1 фоновым цветом, определенным свойством Brush:
with Image1.Canvas do FiliRect (Rect (0, 0, Width, Height));
Метод FindNextControl применяется для определения следующего за указанным в параметре curControl дочернего оконного компонента, соответствующего последовательности табуляции. Если в качестве параметра выступает не дочерний элемент данного оконного компонента, то метод возвращает первый в последовательности табуляции компонент. Второй параметр GoForward определяет направление- поиска компонента. Если этот параметр имеет значение true, то ищется следующий компонент, иначе - предыдущий. Еще один параметр checkTabstop - указывает, будут ли при поиске учитываться компоненты, в которых свойство Tabstop установлено в false. Если значение данного параметра равно true, то такие компоненты не учитываются, иначе - учитываются. Последний параметр checkParent применяется для того, чтобы указывать, учитывать ли при поиске только те компоненты, которые являются прямыми потомками данного оконного компонента. Если этот параметр равен false, то просматриваются все компоненты, иначе - только прямые потомки.
Метод First возвращает первый элемент списка типа TList.
Метод FloodFill применяется для закрашивания замкнутой области канвы произвольной формы каким-либо цветом. В качестве параметров данного метода выступают: начальная точка закрашивания, цвет и стиль заполнения. Начальная точка закрашивания (х, у) должна находиться внутри закрашиваемой области. Два других параметра применяются для задания границы этой области. Параметр color служит для указания цвета, который является границей закрашивания. Параметр Fillstyle может иметь два значения. Если он равен fsSurface, то происходит закрашивание именно той области, которая окрашена цветом Color, а на других цветах закрашивание не происходит. Если же параметр Fiiistyie имеет значение fsBorder, то заполняется область, в которой могут присутствовать любые цвета, кроме color, который является цветом границы закрашивания.
Метод Focused используется для определения, является ли в настоящий момент времени оконный компонент активным. Метод возвращает значение true, если фокус принадлежит данному оконному компоненту, иначе - false.
Метод FrameRect предназначен для рисования на канве прямоугольной рамки. Данный метод использует установки текущей кисти (Brush). Толщина рамки равна одному пикселу. Внутренняя часть рамки не заполняется никаким цветом. В качестве примера, приведем код, который рисует на канве компонента imagei красную прямоугольную рамку:
with Imagei.Canvas do begin
Brush.Color := clRed;
FrameRect ( Rect (10,10,150,100));
end;
Метод Free применяется для вызова деструктора объекта. Этот метод проверяет, не была ли уже ранее высвобождена память, предназначенная для данного объекта, после чего вызывает метод Destroy.
Метод GetTabOrderList предназначен для построения списка типа TList дочерних оконных компонентов, расположенных в последовательности табуляции. Свойство Tabstop во внимание не принимается. В список входят как прямые, так и косвенные потомки данного оконного компонента.
Метод HandleAl located предназначен для проверки наличия дескриптора окна у данного компонента. В случае, когда дескриптор окна есть, метод возвращает значение true. Данный метод удобно применять, если нет необходимости создавать дескриптор окна компоненту, у которого его нет. Непосредственная проверка свойства Handle компонента приводит к созданию дескриптора окна.
Метод HandleNeeded применяется для создания дескриптора окна для компонента, у которого его не было. При работе метод вызывает сначала метод createHandle у родительского компонента, а затем создает дескриптор для данного компонента.
Метод Hide применяется для.того, чтобы сделать компонент невидимым. Вызов этого метода равносилен команде Component.Visible := false;
Если данный компонент является оконным и содержит в себе другие компоненты, то эти компоненты также становятся невидимыми.

Примечание
Обратите внимание на тот факт, что хотя компонент является невидимым, все его свойства и методы остаются доступными.

Метод indexof -применяется для определения индекса первого вхождения в компонент типа TList данного элемента. Если такого элемента в списке нет, метод возвращает значение -1.
Метод insert предназначен для вставки нового элемента списка в заданную позицию. Единственный параметр данного метода index показывает, в какую именно позицию будет вставлен новый элемент списка. При вставке нового элемента все последующие элементы сдвигаются (их индексы увеличиваются на единицу). В случае, когда происходит попытка вставить новый элемент в отсортированный список, генерируется исключение EListError. Тогда лучше использовать метод Add.
Метод invalidate применяется для полной перерисовки компонента, когда с компонентом произошли какие-либо визуальные изменения.
Метод Last возвращает значение, равное последнему указателю списка типа TList, равное значению Count -i.
Метод LineTo применяется для рисования на канве объекта прямой линии. Начало линии совпадает с текущим значением координат пера (penPos) и заканчивающейся в точке с координатами (х, Y), за исключением самой точки, которые передаются в качестве параметров метода.
Метод LoadFromClipboardFormat применяется для загрузки изображения в графический компонент из буфера обмена в формате Windows Clipboard.
Метод LoadFromFiie предназначен для загрузки изображения в графический компонент из файла, задаваемого параметром FileName. Если данный графический файл по каким-либо причинам не может быть загружен (несоответствие типов, незарегистрированный графический формат файла), то генерируется, исключение EInvalidGraphic.
Метод LoadFromstream позволяет загружать графическое изображение из потока, задаваемого параметром stream. Данный метод может использоваться при загрузке, например, графических полей в наборе данных из объекта типа TBlobStream.
Метод Lock применяется для блокировки канвы компонента и запрету рисования на ней из других потоков многопоточного приложения. Обратный результат достигается при помощи метода unlock. При многократном вызове метода Lock будет увеличиваться свойство Lockcount, в котором фиксируется количество блокировок. Канва будет недоступной из других потоков, пока не снимется последняя блокировка. Если вы не хотите использовать многократную блокировку, можно воспользоваться методом TryLock. После блокирования канвы общая производительность приложения может существенно снизиться.
Метод Mask применяется для преобразования цветного изображения в черно-белую маску. В результате замены цвет Transparentcoior переходит в белый, а все остальные цвета в черный.
Метод Move предназначен для перемещения элемента списка, находящегося в позиции, задаваемой параметром Curindex в позицию, задаваемую параметром Newlndex.
Метод MoveTo применяется для изменения текущей позиции пера (Penpos) в заданную параметрами (х, у). При перемещении пера на канве ничего не рисуется. Данный метод аналогичен прямой установке координат пера в свойстве penPos.
Метод OpenBit предназначен для возврата индекса первого элемента массива типа TBits, имеющего значение false.
Метод Pack предназначен для удаления из списка типа TList всех элементов, значение которых равно nil. После удаления происходит переиндексация всех элементов списка.
Метод Pie рисует замкнутый сегмент окружности или эллипса. Параметры данного метода аналогичны параметрам метода Arc. В результате выполнения метода pie может быть рисунок, похожий на рис. 2.5.

Методы

Рис. 2.5. Результат выполнения метода Pie
Метод Play предназначен для воспроизведения заданной последовательности кадров видеоклипа типа AVI. Параметры данного метода: FromFrame, ToFrame и count. Первый параметр задает начальный кадр клипа, второй - конечный кадр клипа, а третий - количество повторений показа данных кадров. В случае, когда count равен нулю, клип будет повторяться до тех пор, пока не будет выполнен метод stop.
Метод Polygon рисует на канве многоугольник по заданному множеству точек, определенных массивом Points, причем первая точка соединяется с последней, после чего многоугольник закрашивается цветом, определенным свойством кисти Brush. Например:
Imagel.Canvas.Polygon ( [ Point (10, 10), Point (30,10),
Point (130, 30), Point (240, 120) ]) ;
Вышеприведенный код рисует на канве компонента image1 закрашенный четырехугольник, координаты которого заданы непосредственно.
Метод PolyLine рисует на канве незамкнутый многоугольник, т. е. кусочно-линейную кривую. Основное отличие этого метода от метода Polygon заключается в том, что PolyLine не соединяет первую и последнюю точки массива Points.
Метод Rectangle предназначен для рисования на канве прямоугольника. В качестве параметров метода передаются координаты двух точек: верхнего левого и правого нижнего углов прямоугольника. Прямоугольник рисуется текущим пером Реп и закрашивается цветом, определенным в свойстве Brush.

Примечание
Для рисования прямоугольника без рамки используйте метод FillRect, а для рисования прямоугольника со скругленными углами - метод RoundRect. Чтобы нарисовать незакрашенный прямоугольник, вызовите метод FrameRect.

Метод Refresh служит для немедленной перерисовки изображения компонента. Данный метод вызывает метод Repaint.
Метод Remove предназначен для удаления элемента со значением равным параметру item из списка типа TList. Данный метод удобно использовать, когда неизвестен индекс удаляемого элемента, а известно лишь его значение. В противном случае можно применять метод Delete.
Метод Repaint применяется для перерисовки изображения компонента. Данный метод можно применять вместо Refresh. Метод Repaint вызывает сначала метод invalidate, а затем update.
Метод RoundRect служит для рисования прямоугольника со скругленными углами. Прямоугольник закрашивается цветом, установленным в свойстве Brush. Два параметра (X1, Y1) и (Х2, Y2) задают координаты углов прямоугольника (как в методе Rectangle). Два других параметра X3 и YЗ задают эллипс с шириной X3 и высотой XЗ. Углы прямоугольника скругляются с помощью данного эллипса.
Метод saveToclipboardFormat применяется для создания копии изображения в формате Windows Clipboard и передачи его в буфер обмена Windows. Однако записать изображение в буфер обмена можно гораздо проще: воспользовавшись методом Assign.
Метод saveToFile сохраняет графическое изображение в файл, задаваемый параметром FileName.
Метод SaveToStream сохраняет графическое изображение в потоке, задаваемом параметром stream.
Метод ScreenTociient служит для преобразования координат экранной области в координаты клиентской части данного компонента.
Метод ScroilBy предназначен для сдвига содержимого данного оконного компонента (включая все его дочерние компоненты). Два параметра Deltax и DeltaY, задают, соответственно, сдвиг по горизонтали и по вертикали. Положительные значения задают сдвиг вправо и вниз, отрицательные - влево и вверх. Например, нижеприведенный код сдвигает содержимое формы Formi на 10 пикселов влево:
Forml.ScroilBy (-10, 0);
Метод SelectFirst предназначен для передачи фокуса компоненту, находящемуся первым в последовательности табуляции. Например, код Forml.SeiectFirst; выберет первый находящийся в последовательности табуляции компонент для формы Forml.
Метод SelectNext передает фокус следующему компоненту, расположенному в последовательности табуляции после указанного в параметре curControl. Второй параметр GoForward определяет направление поиска компонента: если он равен true, то вперед, иначе - назад. Последний параметр checkTabstop определяет, должен ли следующий компонент иметь значение true свойства TabStop.
Метод sendCancelMode предназначен для прекращения модального состояния данного компонента. Модальным состоянием называется такое состояние компонента, когда он ожидает от пользователя какого-либо действия, причем ни один другой компонент приложения не доступен. Вызов данного метода прекращает ожидание действия со стороны пользователя.
Метод sendToBack перемещает указанный компонент в самый конец Z-noследовательности. Таким образом, компонент может стать невидим из-за его перекрытия другими компонентами. Если данный компонент до вызова метода имел фокус, то он его потеряет после выполнения метода.
Метод SetBounds предназначен для одновременного изменения четырех свойств компонента: Left, Top, width и Height. Параметры, соответственно, - ALeft, ATop, AWidth и AHeight. Вызов данного метода позволяет сделать код приложения более компактным. Кроме того, перерисовка компонента произойдет здесь всего один раз, а не четыре, если бы вы изменяли последовательно эти четыре свойства.
Метод SetchiidOrder применяется для изменения позиции компонента, задаваемого параметром child, в списке дочерних компонентов данного оконного компонента. Компоненту присваивается новый индекс, задаваемый параметром order: Пример:
Forral.SetChildOrder (Buttonl, 5);
Таким образом, кнопка Buttonl будет расположена в последовательности дочерних элементов формы Form1 на шестом месте (нумерация начинается с нуля).
Метод SetFocus служит для передачи фокуса данному компоненту. Наример:
Memol.SetFocus;
Метод setzorder предназначен для перемещения данного компонента в начало или конец Z-последовательности. Если параметр TopMost имеет значение true, то компонент перемещается в начало Z-последовательности, иначе в конец.
Метод show используется для того, чтобы сделать видимым невидимый компонент. То есть метод равносилен установке свойства visible данного компонента В true.Метод sort применяется для быстрой сортировки элементов списка типа TList.
Метод stretchDraw применяется для рисования графического изображения, содержащегося в компоненте, указанном параметром Graphic в прямоугольную область канвы, указанную параметром Rect, растягивая или сжимая изображение под размер данной области. Например, нижеприведенный код уменьшает изображение, находящееся в компоненте image2 и имеющее размер больше чем 20x20 точек до размера 20x20 точек, и помещает его в компонент Image 1:
Imagel.Canvas.StretchDraw ( Rect (0,0,19,19), Image2.Picture.Bitmap);
Метод TextExtent применяется для получения и длины, и ширины текста Text, который предполагается вывести на канву данного компонента, используя текущий шрифт. Возвращаемое методом значение имеет тип TSize.
Метод TextHeight возвращает значение, равное высоте текста Text, который предполагается вывести на канву с использованием текущего шрифта.
Метод Textout предназначен для вывода строки текста, задаваемой параметром Text на канву в позицию с координатами (х, Y). Например: Imagel.Canvas.TextOut (10, 100, 'Мне нравится Delphi'); выведет строку "Мне нравится Delphi" на канву компонента image1, начиная с координаты (10, 100).
Метод TextRect похож на метод Textout за исключением того, что текст, выходящий за границы определенной прямоугольной области, урезается.
Метод Textwidth предназначен для определения длины текста Text в пикселах, который предполагается вывести на канву компонента текущим шрифтом.
Метод TryLock блокирует канву компонента, не позволяя другим потокам многопоточного приложения рисовать на ней. Данный метод возвращает значение true и устанавливает свойство LockCount в единицу, если канва не была ранее блокирована. Если канва была ранее блокирована, то метод возвращает false и не увеличивает значение свойства LockCount.
Метод unlock предназначен для разблокирования канвы компонента. Каждый вызов метода Unlock уменьшает значение свойства LockCount на единицу.
Метод update предназначен для немедленной перерисовки компонента, не ожидая завершения каких-либо других процессов.

Поля

Поле (Field) компонента (класса) - это данные, находящиеся в компоненте или классе. Можно представить поле в виде переменной, которая описывается внутри компонента или класса. Например:
type
TMyComponent = class private
FMyFieldl: char; FMyField2: real; FMyFieldS: string; FMyField4: boolean;
end;
На приведенном выше примере, внутри описания компонента TmyComponent, описываются четыре поля FMyFieldl, FMyField2, FMyFieldS И FMyField4, имеющие различный тип. Поля могут быть тех же типов, как и обычные переменные.

Примечание
При создании наследников компонента (класса) они будут наследовать все поля от своего класса предка. Например, на приведенном выше примере компонент TMyComponent будет содержать все поля своего предка - базового класса TObject, и, кроме него, дополнительные четыре поля, описанные выше. Заметим, что удалить или переопределить поля, перешедшие от класса-предка невозможно, поэтому, чем больше предков имеет компонент или класс, тем больше у него полей.


Можно смело утверждать, что все

Можно смело утверждать, что все программирование в Windows связано с какими-либо событиями. Событием может быть движение мышью, нажатие клавиши на клавиатуре или на мыши, закрытие окна и т. д. Программисту остается лишь перехватывать эти события и писать методы, которые будут выполняться при генерации того или иного события.
Событие (event) - это механизм, который связывает системное событие с конкретным кодом, называемым обработчиком события (event handler).
Рассмотрим простой случай, когда происходит системное событие нажатия мышью кнопки на форме. С точки зрения программиста, событие - это всего лишь имя, связанное с системным событием, в нашем случае onclick, которое связано с обработчиком события. Например, кнопка Button1 имеет метод onciick. По умолчанию, Delphi генерирует обработчик события - метод Buttonlclick, связанный с событием Onciick. Программист должен добавить код, который выполняется при нажатии на кнопку Buttoni внутри метода Buttonlclick.
Итак, для наглядного представления процесса обработки рассмотрим простую схему (рис. 2.6).

Можно смело утверждать, что все

Рис. 2.6. Схема обработки события

Стандартные события

Рассмотрим основные события, учитываемые компилятором Delphi. Для начала перечислим эти события:
OnChange

OnEnter

OnMouseMove

OnClick

OnExit

OnPaint

OnDblClick

OnKeyDown

OnProgress

OnDragDrop

OnKeyPress

OnStartDrag

OnDragOver

OnKeyUp

OnMouseUp

OnEndDrag

OnMouseDown


Рассмотрим каждое событие более подробно.
Событие onchange наступает после изменения какого-либо графического объекта. Создавайте обработчик такого события для выполнения каких-либо операций, происходящих после изменения графического объекта.
Событие onclick компонента наступает в случае, если пользователь нажал и отпустил левую кнопку мыши в тот момент, когда указатель мыши находился на компоненте. Кроме того, событие Onciick происходит в следующих случаях:
- при выборе пользователем, путем нажатия клавиш управления курсором, элемента в сетке (Grid), дереве (Tree), списке (List) или выпадающем списке (DropDown List);
- при нажатии пользователем клавиши <Пробел> или в тот момент, когда компонент (например, кнопка) был в фокусе (component.Focused = True);
- при нажатии пользователем клавиши , в случае, когда активная форма имеет кнопку по умолчанию;
- при нажатии пользователем клавиши , в случае, когда активная форма имеет кнопку прерывания;
- при нажатии пользователем комбинации клавиш быстрого доступа ("горячих" клавиш) для обращения к кнопке или пункту меню. Например, в свойстве caption кнопки формы записано &пуск, при этом надпись на кнопке имеет вид Пуск. В результате, когда пользователь нажимает комбинацию клавиш +<П>, происходит событие onclick;
- при установке приложением свойства checked переключателя RadioButton В true;
- при изменении приложением свойства Checked индикатора checkBox; - при вызове метода click элемента меню приложения.
Событие onciick возникает для формы в случае, когда пользователь щелкнул на любом месте формы, незанятом компонентами.
Событие onDblclick наступает в случае, когда пользователь дважды щелкнул левой кнопкой мыши на компоненте, причем отпустил кнопку мыши после второго щелчка над компонентом.

Примечание
К одному и тому же компоненту нельзя написать обработчики событий Onclick и OnDbiciick, поскольку первый из них всегда перехватит первый из щелчков. Событие OnStartDrag наступает, когда пользователь начинает перетаскивать компонент, т. е. нажал над компонентом левую кнопку мыши и, не отпуская ее, начал смещать курсор мыши. Событие имеет параметр Sender, содержащий наименование компонента, который должен перетаскиваться или который содержит объект перетаскивания (в случае, когда компонент является компонентом контейнерного типа).

Событие onDragDrop компонента наступает, когда пользователь отпускает перетаскиваемый компонент над компонентом. В обработчике события нужно описать, что должно происходить в момент отпускания перетаскиваемого компонента. При этом параметр source должен соответствовать перетаскиваемому компоненту, а параметр Sender - компоненту, над которым компонент будет отпущен. Кроме того, два параметра х и Y служат для хранения координат курсора мыши над компонентом. Система координат, в данном случае, соответствует клиентской части компонента.
Событие onDragOver компонента наступает, когда перетаскиваемый компонент пересекает границу данного компонента и оказывается над ним. Это событие возникает все время, пока пользователь перемещает компонент над компонентом-приемником. Как только пользователь отпускает компонент (отпускает левую кнопку мыши) происходит событие OnDragDrop, описанное выше. Для того чтобы определить, компоненты какого типа принимает данный компонент, используется параметр Accept. Если компонент способен принимать любые компоненты, то можно оставить обработчик OnDragOver пустым, но он обязательно должен присутствовать. Например:
procedure TForml.ListBox2DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean); begin
// Данный комментарий нужен, чтобы компилятор не удалил этот пустой // обработчик
end;
Во время перетаскивания компонента форма указателя мыши может изменяться. Для установки этого свойства служит свойство DragCursor компонента, на который будет переноситься другой компонент.
Приведем простой пример использования событий OnDragDrop и OnDragOver.
Для наглядного применения события OnDragDrop создадим приложение, которое позволит пользователю перетаскивать строки одного списка в другой. Итак, расположим на форме два списка ListBox: ListBoxi и ListBox2. Добавим строки в первый список путем редактирования его свойства items. Назовем строки Строка 1. . .Строка 10 (рис. 2.7).
Для простоты будем перетаскивать строки из первого списка во второй. Поменяем значение СВОЙСТВа DragMode СПИСКа ListBoxi на dmAutomatic, ЧТО обеспечит автоматическое начало перетаскивания. Теперь для второго списка (ListBox2) напишем Обработчик события OnDragOver (Листинг 2.2).

Листинг 2.2
procedure TForml.ListBox2DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := Source is TListBox;
end;

Стандартные события

Рис. 2.7. Пример применения событий OnDragDrop и OnDragOver
В данном обработчике мы указываем, что на компонент ListBox2 можно перетаскивать компоненты типа TListBox. Затем в обработчике события OnDragDrop запишем следующий код (листинг 2.3):

Листинг 2.3
procedure TForml.ListBox2DragDrop(Sender, Source: TObject; X, У: Integer);
begin
ListBox2.Items.Add(ListBoxl.Items[ListBoxl.Itemlndex]);
end;

Таким образом, мы добавляем выбранную строку компонента ListBoxl в компонент ListBox2.
Все! Можно запускать приложение. Попробуйте перетащить любую строку из первого списка во второй.
Событие OnEndDrag - последнее из событий, которые предназначены для обработки переноса одного компонента на другой. Оно наступает при любом окончании процесса переноса, как успешного, так и неудачного (когда компонент отпущен над формой или компонентом, не способном его принять). Данное событие наступает в перетаскиваемом компоненте. Событие
OnEndDrag. может применяться для реакции приложения на перетаскивание (например, "выполнено успешно" или "неудача"). В обработчике данного события параметр Sender - это сам объект перетаскивания, а параметр Target принимает значение компонента-приемника (при успешном перетаскивании) или nil (при неудачном переносе). Приведем пример (листинг 2.4).

Листинг 2.4
procedure TForml.ComponentlEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
If Target = Nil then ShowMessage('Перенесение объекта '+
(Sender as TControl).Name + ' завершилось неудачно') else
ShowMessage((Sender as TControl).Name + ' перенесен в ' + (Target as TControl).Name);
end;
Добавим код, записанный в листинге 2.3, в вышеописанное приложение. При этом код нужно поместить в обработчике события OnEndDrag для первого списка (ListBox1). В результате, при каждом успешном перетаскивании строки из первого списка во второй будет выдаваться окно-сообщение (рис. 2.8), а при неудачном - окно-сообщение, изображенное на рис. 2.9.

Стандартные события

Рис. 2.8. Окно, выдаваемое при успешном перетаскивании строки из ListBox1 в ListBox2

Стандартные события

Рис. 2.9. Окно, выдаваемоe при неудачном перетаскивании строки из ListBox1
Событие OnEnter происходит, когда компонент получает фокус. Данное событие не наступает при переключении между разными формами приложения или между различными приложениями. При переключении между компонентами контейнерного типа (т. е. между компонентами, которые могут размещать на себе другие компоненты, например панели) событие OnEnter наступает сначала для компонента контейнерного типа, а затем для содержащегося в нем компонента.
Событие OnExit является противоположным по отношению к OnEnter. Оно наступает в момент, когда компонент теряет фокус, т. е. когда фокус переходит к другому компоненту. Это событие также не наступает при переключении между разными формами или приложениями. В отличие от события OnEnter, событие OnExit наступает сначача для компонента, содержащегося в компоненте-контейнере, а затем для самого компонента контейнерного типа.
Приведем пример, иллюстрирующий события OnEnter и OnExit. Расположим на форме кнопку Button1 и группу переключателей RadioGroupi (рис. 2.10). Добавим в группу переключателей несколько строк (путем редактирования свойства items).

Стандартные события

Рис. 2.10. Пример, иллюстрирующий работу событий OnEnter и OnExit
Запустим приложение. Фокус при запуске будет передан компоненту, который был размещен на форме первым (в нашем случае - это кнопка Button1). Если теперь выбрать щелчком мыши любой переключатель группы переключателей, то произойдет следующее:
И для кнопки Button1 наступит событие OnExit;
- для группы переключателей RadioGroupi - событие OnEnter;
- наконец, для выбранного переключателя из группы - событие OnEnter.
Если после этого выбрать щелчком мыши кнопку Button1, то события произойдут в таком порядке:
- для активного переключателя группы наступит событие OnExit; - для группы переключателей - событие OnExit;
- для кнопки Button1 - событие OnEnter.
Событие onKeyDown наступает, когда пользователь нажимает любую клавишу. Данное событие наступает для компонента, имеющему фокус в момент нажатия кнопки. С помощью этого события можно обрабатывать все клавиши, включая , и . В процедуру-обработчик передаются, кроме параметра sender, такие параметры, как Key и shift. Параметр Key определяет нажатую клавишу. В случае, если нажата не алфавитно-цифровая клавиша, в параметр передается виртуальный код клавиши. Приведем таблицу кодов клавиш (табл. 2.7).
Таблица 2.7. Коды клавиш
Клавиша

Десятичное число

Шестнадцатеричное число

Символическое имя



112

70

VK F1



113

71

VK_F2



114

72

VK_F3



115

73

VK_F4



116

74

VK_F5



117

75

VK_F6



118

76

VK F7



119

77

VK_F8



120

78

VK_F9



121

79

VK F10

<Пробел>

32

20

VK SPACE



8

8

VK_BACK



9

9

VKJTAB



13

OD

VK_RETURN



16

10

VK SHIFT



17

11

VK_CONTROL



18

12

VK_MENU-



20

14

VK_CAPITAL

<Еsc>

27



VK ESCAPE



45

2D

VK INSERT



33

21

VK PRIOR



34

22

VK_NEXT



35

23

VK END



36

24

VK HOME

<Стрелка влево

37

25

VK_LEFT

<Стрелка вверх>

38

26

VK UP

<Стрелка вправо>

39

27

VK_RIGHT

<Стрелка вниз>

40

28

VK DOWN



46

2E

VK_DELETE



44

2C

VKJ3NAPSHOT



145

91

VK SCROLL



19

13

VK_PAUSE



144

90

VK_NUMLOCK

На цифровой клавиатуре, при выключенном режиме NumLock
<0>

96

60

VK_NUMPADO

<1>

97

61

VK_NUMPAD1

<2>

98

62

VK_NUMPAD2

<3>

99

63

VK_NUMPAD3

<4>

100

64

VK_NUMPAD4

<5>

101

65

VKJTOMPAD5

<6>

102

66

VK_NUMPAD6

<7>

103

67

VK_NUMPAD7

<8>

104

68

VK_NUMPAD8

<9>

105

69

VK_NUMPAD9

<*>

106



VK_MULTIPLY

<+>

107



VK_ADD

<->

109

6D

VK_SUBTRACT

<.>

110



VK_DECIMAL



111

6F

VK_DIVIDE


Данные коды не различают регистр и раскладку клавиатуры (латинская/кириллица). Параметр shift является множеством, которое может быть пустым или содержать следующие элементы:
- ssshift - при нажатой клавише ;
- ssAlt - при нажатой клавише ;
- ssctrl - при нажатой клавише .
Приведем пример использования события OnKeyDown. Предположим, что нам необходимо распознать, когда пользователь нажмет комбинацию клавиш ++. В обработчике события OnKeyDown напишем следующий код:
if ((Key = ord CL1)) and (ssShift in Shift) and (ssCtrl in Shift)) then ShowMessage ('Нажата комбинация клавиш ++');
В вышеприведенном примере мы использовали функцию ord (), которая позволяет по символу клавиши получить код клавиши (в нашем случае, код клавиши ). Теперь, всякий раз, когда фокус будет у компонента, к которому привязан данный обработчик, и когда пользователь нажмет комбинацию клавиш ++, будет выводиться окно (рис. 2.11).

Стандартные события

Рис. 2.11. Окно, появляющееся при обработке события OnKeyDown
Событие onKeypress наступает при нажатии пользователем символьной клавиши. Данное событие имеет параметр Key, который содержит символ нажатой клавиши и имеет тип char. При этом различаются символы верхнего и нижнего регистров, а также раскладка клавиатуры.

Примечание
С помощью события OnKeyPress невозможно обработать нажатие функциональных клавиш и клавиш , и . Таким образом, когда вы нажимаете комбинацию клавиш +, в параметр Key события OnKeyPress поступит значение "В", клавиша только поменяет регистр символа. При нажатии комбинации клавиш, включающих клавишу , событие OnKeyPress при нажатии комбинации клавиш <АИ>+<любая символьная клавиша> не наступает. При нажатой комбинации клавиш <С1г1>+<любая символьная клавиша> событие OnKeyPress наступает, но в параметр Key ничего не передается.

Событие OnKeyUp происходит при отпускании пользователем любой ранее нажатой клавиши. Данное событие позволяет обрабатывать все клавиши, как и событие OnKeyDown. По своим параметрам и поведению событие OnKeyUp равносильно событию OnKeyDown.
Событие onMouseDown наступает при нажатии пользователем любой кнопки мыши в тот момент, когда указатель мыши находится над компонентом. Данное событие имеет параметры Button, shift, x и Y. Параметр Button определяет, какая кнопка мыши нажата:
- mbLeft - левая кнопка; - mbMiddle - средняя кнопка; - mbRight - правая кнопка.
Параметр shift равносилен параметру shift для событий, связанных с обработкой клавиатуры. Таким образом, можно обрабатывать нажатие любой кнопки мыши одновременно с клавишами , или .
Параметры х и Y содержат координаты указателя мыши в области компонента.
Событие onMouseUp наступает, когда пользователь отпускает любую кнопку мыши над компонентом. По своим функциям и параметрам данное событие аналогично событию onMouseDown.
Событие onMouseMove наступает при перемещении указателя мыши над компонентом. Это событие возникает'независимо от того, нажаты какие-либо клавиши мыши или нет.

Примечание
При нажатой левой кнопке мыши данное событие не возникает. Эта особенность почему-то не документирована.

Данное событие имеет следующие параметры: shift, х и Y, аналогичные вышеописанным.
Событие onPaint. наступает, когда приложение получает сообщение Windows о необходимости перерисовки испорченного изображения. Изображение может испортиться от перекрытия окон одного или нескольких приложений. В обработчик данного события программист должен разместить процедуру, выполняющую перерисовку изображения. Например, если на форме был размещен рисунок, хранящийся в компоненте BitMap, тогда можно использовать следующий обработчик события OnPaint для перерисовки изображения:
Canvas.Draw (О, О, BitMap);
Событие OnProgress наступает при прохождении медленных процессов, связанных с изменением графического изображения. Данное событие позволяет строить индикаторы хода выполнения процесса. Событие OnProgress имеет следующие параметры: stage, PercentDone, RedrawNow, R и Msg. Параметр stage предназначен для указания стадии прогресса (начало, продолжение, окончание) и может принимать значения psstarting (начало), psRunning (продолжение), psEnding (окончание). Параметр PercentDone показывает, какая часть процесса выполнена. Параметр RedrawNow говорит, может ли в настоящий момент изображение успешно отобразиться на экране. Параметр R служит для указания области изображения, которая изменена и требует перерисовки. Наконец, параметр Msg служит для отображения сообщений о ходе процесса. Этот параметр имеет строковый тип. Параметр Msg может быть пустым.

Структура компонентов

Компоненты, как и всякий другой класс, инкапсулируют в себе свойства, события, методы и поля.

Примечание
Компоненты являются классами, с которыми можно работать в интегрированной среде разработки Delphi.

Рассмотрим свойства, события, методы и поля компонентов.

Свойства стандартных компонентов Delphi

Рассмотрим свойства, которые имеют стандартные компоненты Delphi.
Свойство Action предназначено для определения действия, связанного с элементом управления (меню, кнопкой и т. д). Значение данного свойства устанавливается во время разработки приложения, путем выбора из выпадающего списка предусмотренных действий. Данный список формируется путем размещения на форме компонента TActionList и задания его свойств.
Свойство Active предназначено для определения наличия соединения с базой данных, а также для установки или отключения этого соединения. Данное свойство может принимать одно из двух значений: false или true. Если значение свойства равно false, то в этом случае база данных является закрытой и работа с ней (чтение или запись данных) невозможна. Если вы установите свойство Active в true, то произойдет следующее:
- приложение сгенерирует событие BeforeOpen;
- набор данных (свойства state) установится в состояние dsBrowse;
- в наборе данных установится курсор;
- приложение сгенерирует событие AfterOpen.
Данное свойство можно установить непосредственно, например:
Tablel.Active := true;
или с помощью вызова методов open (установка свойства Active в true) или
Close (false): Tablel.Open;
Свойство Align предназначено для задания способа выравнивания компонента внутри компонента контейнерного типа. Перечислим возможные значения данного свойства.
- alNone - компонент остается на том месте, где он был размещен во время разработки приложения. Данное значение присваивается свойству Align по умолчанию.
- alTop - компонент занимает всю верхнюю часть компонента-контейнера. Во время выполнения приложения ширина компонента зависит от ширины компонента-контейнера. Высота компонента остается неизменной независимо от высоты контейнера.
- alBottom - аналогичен значению aiTop, за исключением того, что компонент занимает всю нижнюю часть компонента-контейнера.
- alLeft - компонент занимает всю левую часть компонента-контейнера. Во время выполнения приложения высота компонента зависит от высоты компонента-контейнера. При изменении ширины компонента-контейнера ширина компонента остается неизменной.
- alRight - аналогичен значению alLeft, за исключением того, что компонент занимает всю правую часть компонента-контейнера.
- alclient - компонент занимает всю клиентскую часть компонента-контейнера. При изменении высоты и ширины компонента-контейнера меняется высота и ширина компонента. Если в клиентской части компонента контейнерного типа уже имеются другие компоненты, то данный компонент занимает всю оставшуюся незанятую часть компонента-контейнера.

Примечание
Значения alTop и alBottom имеют больший приоритет по сравнению со значениями alLeft и aiRight. Таким образом, если вы вывели на форму два компонента, присвоив свойству Align одного из них значение alTop, а другого - alRight, то первый компонент вытеснит верхнюю часть второго компонента (рис. 2.1).

Свойство Anchors предназначено для определения привязки компонента к родительскому компоненту при изменении размеров родительского компонента. Данное свойство было введено начиная с Delphi 4. Это свойство имеет тип множества, которое может содержать такие элементы:
- akTop - компонент привязан к верхнему краю родительского компонента; - akLeft - компонент привязан к левому краю родительского компонента;
- akBottom - компонент привязан к нижнему краю родительского компонента;
- akRight - компонент привязан к правому краю родительского компонента.

Свойства стандартных компонентов Delphi

Рис. 2.1. Применение свойства Align
В случае, когда существует привязка к противоположным сторонам родительского компонента, при изменении его размеров будет происходить сжатие или растяжение дочернего компонента вплоть до полного исчезновения его изображения. Таким образом, данное свойство определяет как бы фиксацию расстояния от компонента до краев родительского компонента.
Свойство AutoCalcFields определяет режим автоматической генерации события onCalcFields. Данное свойство установлено в true по умолчанию. В этом случае события onCalcField, связанные с пересчетом вычисляемых полей таблицы данных, генерируются в следующих случаях:
- когда открывается набор данных;
- когда набор данных переводится в состояние dsEdit;
- когда фокус переходит между компонентами, содержащими данные или перемещается по столбцам таблицы, связанной с данными;
- когда ищется какая-либо запись.
Для того чтобы уменьшить время, необходимое на пересчет вычисляемых полей, можно установить свойство AutoCalcField В false.
Свойство AutoEnable служит для задания автоматического определения доступных кнопок медиаплейера в разных режимах. Если это свойство установлено в true, то значение, заданное свойством EnabledButtons, игнорируется. В противном случае доступные кнопки определяются свойством EnabiedButtons (описание этого свойства см. далее).
Свойство AutoMerge применяется для того, чтобы объединить меню вторичных форм c меню главной формы. Для этого нужно в каждой вторичной форме установить данное свойство в true.

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

Свойство AutoOpen предназначено для автоматического открытия медиа-плейера во время выполнения приложения. Если данному свойству присвоить значение true, то медиаплейер автоматически откроет при создании формы устройство, указанное в свойстве DeviceType (описание этого свойства см. далее). В случае если при открытии устройства возникает ошибка, то приложение генерирует исключение EMClDeviceError. Если значение свойства AutoOpen равно false, то для открытия устройства необходимо использовать метод Open.
Свойство AutoSelect показывает, будет ли текст, содержащийся в компоненте, выделяться в тот момент, когда данный компонент получает фокус. Это свойство не применяется в многострочных редакторах (таких как Memo, RichEdit и др.). Если данное свойство имеет значение true, то текст, содержащийся в однострочном редакторе, будет выделен при получении фокуса. Свойство AutoSelect удобно применять, когда пользователь, скорее всего, будет вводить новое значение в поле для ввода вместо старого, а не редактировать.
Свойство AutoSize применяется для того, чтобы приложение автоматически устанавливало высоту поля для ввода (Edit), в зависимости от высоты используемого шрифта. Если значение данного свойства равно true, то поле для ввода будет автоматически изменять размер при смене размера используемого в нем шрифта.
Свойство Bitmap предназначено для указания на объект типа TBitmap, который содержит изображение. В случае если свойство Bitmap не пустое, то свойство style игнорируется и в качестве шаблона заполнения берется объект, на который указывает свойство Bitmap.
Свойство BoundsRect применяется для получения одновременно координат всех четырех вершин компонента.

Примечание
Данные координаты можно получить из следующих свойств компонента: Left (левый край компонента), тор (верхний край), width (ширина компонента) и Height (высота). Описание этих свойств мы приведем ниже.

Началом координат считается левый верхний угол окна, содержащего данный компонент.
Свойство Break предназначено для разбивания списка разделов меню на несколько столбцов. Это свойство может принимать следующие значения:
- mbNone - нет разбивки (установлено по умолчанию);
- mbBarBreak - в меню добавляется новый столбец разделов и отделяется от предыдущего полосой;
- mbBreak - в меню добавляется новый Столбец разделов и отделяется от предыдущего пробелами.
Рассмотрим пример (рис. 2.2).
В данном примере мы отделили Первый раздел меню от Второго полосой (установили значение свойства Break для Второго раздела в mbBarBreak), а также отделили Третий и Четвертый разделы меню от Второго пробелами (установили значение свойства Break для Третьего раздела в mbBreak).

Свойства стандартных компонентов Delphi

Рис. 2.2. Разбиение меню приложения на столбцы разделов с помощью свойства Break
Свойство Brush применяется для чтения текущего значения цвета и стиля заполнения фона окна. Обратите внимание на то, что данное свойство предназначено только для чтения. Но значение этого свойства можно изменить при помощи изменения свойств Color и style.
Свойство Canvas предназначено для рисования, изменения изображения и других действий с изображениями. Данное свойство имеется у тех компонентов, которые имеют поверхности для рисования.
Свойство Capabilities определяет возможности открытого устройства мультимедиа. Данное свойство устанавливается при открытии устройства мультимедиа методом Open. Это свойство доступно только для чтения. Свойство capabilities может принимать следующие значения:
- mpCanEject - возможно освобождение объекта, загруженного в устройство; - mpCanPlay - возможно воспроизведение; - mpCanRecord - возможна запись;
- mpcanstep - возможно перемещение на задаваемое число кадров вперед и назад;
- mpuseswindow - возможно использование окна для отображения выходного изображения.
Свойство capacity применяется для определения количества элементов, которые могут храниться в компоненте класса TList. В случае, когда при увеличении значения данного свойства возникает нехватка оперативной памяти, генерируется исключение EOutOfMemory. Свойство capacity отличается от похожего свойства Count тем, что хранит в себе число, указывающее, сколько элементов может храниться в Компоненте класса TList, a count показывает, сколько элементов действительно хранится в компоненте. Таким образом, значение свойства capacity всегда больше или равно значению свойства count. При увеличении значения свойства count, в случае необходимости, происходит увеличение значения Capacity. Данное свойство применяется для того, чтобы предотвратить слишком частое перераспределение оперативной памяти. Если принудительно задать значение свойства capacity меньше, чем значение свойства count, приложение сгенерирует исключение EListError.
Свойство caption связывает с компонентом некоторую строку текста, поясняющую его назначение. Данная строка является обычно заголовком компонента (заголовком кнопки, метки, пункта меню и др.). По умолчанию это свойство устанавливается таким же, как имя компонента (свойство Name). Для пунктов меню и кнопок с помощью этого свойства можно задать кнопку быстрого вызова. Для этого перед символом кнопки быстрого вызова ставится символ амперсанда (&). Например, для быстрого вызова меню Файл перед буквой "Ф" в свойстве caption данного пункта меню можно поставить знак &: &Файл. При этом буква "Ф" в пункте меню будет подчеркнутой. Теперь для вызова данного пункта меню достаточно нажать комбинацию клавиш <А1t>+<Ф>.
Свойство charset применяется для задания используемого набора символов шрифта. Каждый вид шрифта поддерживает один или несколько наборов символов. Ниже приведена сокращенная таблица констант, применяемых в данном свойстве (табл. 2.2).
Таблица 2.2. Константы наборов символов
Константа

Числовое значение

Набор символов

ANSI CHARSET

0

Символы ANSI

DEFAULT_CHARSET

1

Символы, установленные по умолчанию. Шрифт выбирается только по имени (Name) и размеру (size)

SYMBOL CHARSET

2

Стандартный символьный набор

MAC__CHARSET

77

Набор символов Macintosh

GREEK CHARSET

161

Набор греческих символов

RUSSIAN_CHARSET

204

Кириллица

EASTEUROPE CHARSET

238

Набор, включающий в себя символы, добавляемые к основным буквам для восточноевропейских языков

OEM_CHARSET

255

Набор символов операционной системы


В некоторых случаях, для того, чтобы правильно отображались русские шрифты, полезно установить значение свойства charset в RUSSIAN_CHARSET.
Свойство clientHeight предназначено для установки или чтения высоты клиентской области компонента. Данное свойство применяется при изменении размеров компонента, который содержит в себе другие компоненты. При таком изменении будет происходить изменение компонентов, содержащихся в компоненте-контейнере.
Свойство clientorigin предназначено для получения экранных координат (х и у) левого верхнего угла клиентской области компонента. Началом координат является верхний левый угол экрана. Возвращаемые координаты передаются в структуре типа TPoint. Данное свойство предназначено только для чтения.
Свойство clientRect возвращает координаты углов клиентской области компонента. Данное свойство также предназначено только для чтения. Координаты возвращаются в структуре типа TRect.
Свойство clientwidth предназначено для установки или чтения горизонтального размера клиентской области компонента.
Свойство ciipRect определяет область, доступную для рисования. То есть вне области, определяемой свойством clipRect, рисовать невозможно. Использование данного свойства сокращает затраты времени на перерисовку этой области.
Свойство color определяет цвет фона компонента. Значение этого свойства может быть или числом, определяющим интенсивность трех основных цветов (красного, зеленого и синего), или константой, определенной в Delphi (табл. 2.3).
Таблица 2.3. Константы, определяющие цвета
Константа

Цвет

ClBlack

Черный

CIMaroon

Темно-бордовый

ClGreen

Зеленый

ClOlive

Оливковый

CINavy

Темно-синий

Cl Purple

Пурпурный

CITeal

Морской

CIGray

Серый

CISilver

Серебристый

CIRed

Красный

CILime

Лимонный

ClBlue

Синий

ClYellow

Желтый

ClFuchsia

Сиреневый

СlAqua

Голубой

ClWhite

Белый

ClBackground

Цвет фона рабочего стола Windows

CIScrollBr

Цвет полос прокрутки

ClActiveCaption

Цвет фона полосы заголовка активного окна

CllnactiveCaption

Цвет фона полосы заголовка неактивного окна

CIMenu

Цвет фона меню

ClWindow

Цвет фона окна

ClWindowFrame

Цвет рамки окна

CIMenuText

Цвет текста меню

ClWindowText

Цвет текста окна

CICaptionText

Цвет текста заголовка в активном окне

ClActiveBorder

Цвет бордюра активного окна

ClInactiveBorder

Цвет бордюра неактивного окна

ClAppWorkSpace

Цвет рабочей области приложения

ClHighlight

Цвет фона выделенного текста

ClHighlightText

Цвет выделенного текста

ClBtnFace

Цвет поверхности кнопки

ClBtnShadow

Цвет тени, отбрасываемой кнопкой

CIGrayText

Цвет текста недоступных элементов окна

ClBtnText

Цвет текста кнопки

ClInactiveCaptionText

Цвет заголовка в неактивном окне

ClBtnHighlight

Цвет выделенной кнопки

C13DdkShadow

Цвет темных теней трехмерных элементов окна

ClBDlight

Светлый цвет на краях трехмерных элементов окна

ClInfoText

Цвет текста советов

ClInfoBk

Цвет фона советов


Данные константы цвета, начиная с clBackground, определяются той цветовой схемой Windows, которая установлена пользователем на данном конкретном компьютере. Эти цвета рекомендуется использовать при разработке приложений для передачи третьим лицам.
Свойство commonAvi предназначено для определения стандартного клипа Windows из файла-библиотеки Shell32.dll. То есть такие клипы, как копирование файлов, поиск файлов, удаление файлов в корзину и др.
Свойство componentcount определяет число компонентов, содержащихся в данном компоненте контейнерного типа.
Свойство component index определяет индекс (порядковый номер) компонента, содержащего в компоненте-контейнере.
Свойство components хранит в себе массив компонентов, владельцем которых является данный компонент. Параметр свойств index позволяет выбрать любой компонент из массива, благодаря свойству Componentindex. Индексы компонентов нумеруются начиная с нуля. Число компонентов, которые содержатся в массиве, можно определить с помощью свойства Componentcount.
Рассмотрим пример применения данных свойств. Предположим, что у нас на форме имеется несколько компонентов. Наша задача - сместить все компоненты, которые присутствуют на форме, кроме компонента Button1 вправо на 10 единиц. Для этого мы можем использовать следующий код:
for i := 0 to Componentcount - 1 do
if (Components[i].Name <> 'Button1') then
(Components[i] as TControl).Left:=(Components[i] as TControl).Left + 10;
Свойство controicount определяет число дочерних компонентов данного оконного элемента. Дочерними называются компоненты, которые расположены в клиентской области этого элемента и, кроме того, в свойстве Parent которых указан данный элемент. Свойство controicount предназначено только для чтения.
Свойство controls является массивом, содержащим все дочерние компоненты данного элемента. Параметр index определяет индекс соответствующего компонента. Индексы начинают нумероваться с нуля. Это свойство предназначено только для чтения.
Свойство Controlstate определяет различные условия, действующие на данный экземпляр компонента. Например, двойной щелчок мыши или необходимость выравнивания компонента.
Свойство Controlstyle определяет различные атрибуты компонента. Например, может ли компонент быть захвачен мышью, имеет ли компонент фиксированные размеры и др. Данное свойство описывает не экземпляры класса, а класс в целом.
Свойство CopyMode определяет способ копирования графического изображения на канву с помощью метода copyRect, или при рисовании объекта типа TBitmap. При помощи данного свойства можно достичь разных эффектов объединения изображений. В табл. 2.4 перечислены возможные значения свойства CopyMode.
Таблица 2.4. Возможные значения свойства CopyMode
Значение свойства

Действие при копировании

cmBlackness

Заполняет область, в которую производится копирование, черным цветом. Собственное изображение и копируемое изображение удаляются

cmDstlnvert

Инвертирует изображение области копирования. Копируемое изображение может быть любым - оно не участвует в процессе инвертирования

cmMergeCopy

Объединяет изображение, находящееся в области копирования, и копируемое изображение при помощи булевой операции AND

cmMergePaint

Объединяет изображение, находящееся в области копирования, и инвертированное копируемое изображение при помощи булевой операции OR

cmNotSrcCopy

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

crnNotSrcErase

Объединяет изображение, находящееся в области копирования, и копируемое изображение при помощи булевой операции OR, после чего результат копирования инвертируется

cmPatCopy

Копирует шаблон источника в область копирования, причем собственное изображение игнорируется

cmPatlnvert

Объединяет изображение, находящееся в области копирования, и шаблон источника при помощи булевой операции XOR

cmPatPaint

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

cmSrcAnd

Объединяет изображение, находящееся в области копирования, и изображение источника при помощи булевой операции AND

cmSrcCopy

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

cmSrcErase

Инвертирует изображение в области копирования и объединяет результат с изображением источника при помощи булевой операции AND

cmSrcInvert

Объединяет изображение источника с изображением в области копирования при помощи булевой операции XOR. Повторное копирование восстанавливает прежнее изображение в области копирования

cmSrcPaint

Объединяет изображение в области копирования и изображение источника при помощи булевой операции OR

cmWhiteness

Заполняет область копирования белым цветом, собственное изображение и копируемое изображение игнорируются


Приведем пример использования способов копирования. Предположим, нам нужно в область с изображением вывести некоторое изображение, а затем убрать его, восстановив прежнее изображение. Для этого можно использовать код:
Imagel.Canvas.CopyMode := cmSrcInvert;
Imagel.Canvas.CopyRect (Rect (0,0,200,200), Image2.Canvas,
Rect (0,0,200,200)); Imagel.Canvas.CopyRect (Rect (0,0,200,200), Image2.Canvas,.
Rect '(0,0,200,200));
Свойство count предназначено для хранения количества элементов, содержащихся в компоненте класса TList. При добавлении новых элементов или при удалении (при помощи методов Add и Delete) значение данного свойства изменяется автоматически. При увеличении значения данного свойства вручную, в компонент класса TList будет добавлено соответствующее значение нулевых элементов. Удалить нулевые элементы можно при помощи вызова метода Pack. При уменьшении данного свойства count будут удалены последние элементы, содержащиеся в данном компоненте.
Свойство cti3D предназначено для установки внешнего вида компонента. Если данное свойство имеет значение true, то компонент будет выглядеть бъемным, если - false, то компонент будет плоским.
Свойство Cursor определяет вид указателя мыши, когда он находится над данным компонентом. Есть еще одно свойство Dragcursor, которое определяет вид указателя мыши, когда над компонентом происходит перетаскивание другого компонента. Delphi имеет встроенные виды указателей. Кроме встроенных, разработчик может помещать в приложение собственные виды указателя мыши. В табл. 2.5 находится список встроенных типов указателей мыши.
Таблица 2.5. Встроенные указатели мыши
Значение свойства Cursor

Изображение указателя мыши

crDefault

Указатель, принятый по умолчанию. Обычно это crArrow

crNone

Без изображения указателя

crArrow

Стрелка

crcross

Крест

crIBeam

Указатель в виде курсора для ввода текста

crSize

Указатель изменения размера окна

crSizeNESW

Указатель изменения размера окна в правом верхнем углу или в левом нижнем

crSizeNS

Указатель изменения размера окна вверху или внизу (по вертикали)

crSizeNWSE

Указатель изменения размера окна в левом верхнем углу или в правом нижнем

crSizeWE

Указатель изменения размера окна слева или справа (по горизонтали)

crUpArrow

Стрелка вверх

crHourGlass

Песочные часы

crDrag

Перетаскивание компонента

crNoDrop

Указатель, показывающий невозможность переноса компонента в данную область

crHSplit

Горизонтальный разделитель

crVSplit

Вертикальный разделитель

crMultiDrag

Перетаскивание нескольких компонентов

crSQLWait

Ожидание выполнения SQL-запроса

crNo

Указатель, показывающий невозможность действия

crAppStart

Ожидание старта приложения (стрелка с песочными часами)

crHelp

Стрелка с вопросом

crHandPoint

Указатель в форме руки


Свойство DesktopFont предназначено для определения, использует ли компонент для отображения текста шрифт Windows. Установка свойства DesktopFont в True определяет, что компонент должен применять для свойства Font изображение шрифта Windows. Этот шрифт задается свойством lconFont глобальной переменной Screen. Таким образом, всякий раз, когда будет изменяться используемый Windows шрифт, будет автоматически изменяться свойство Font для данного компонента.
Свойство DeviceType применяется для задания типа устройства мультимедиа, открываемого медиаплейером. По умолчанию значение данного свойства dtAutoSelect. Это означает, что тип устройства будет определяться по расширению файла, указанного в свойстве FileName.
Свойство Display определяет компонент окна, который будет использован мультимедийным устройством для вывода. Таким компонентом может быть форма или панель. По умолчанию значение данного свойства равно nil, это означает, что устройство создает собственное окно для вывода. Например, если мы хотим, чтобы изображение выводилось на панель Panel 1, мы можем использовать следующий код:
MediaPlayerl.Display := Panel1;
Свойство DispiayRect предназначено для задания прямоугольной области окна, определенного свойством Display, для вывода в нее изображения.
Данное cвойство игнорируется, если Display = nil. свойство DispiayRect должно задаваться только после того, как устройство мультимедиа открыто. При задании значения данного свойства обратите внимание на то, что первые два числа задают координаты левого верхнего угла изображения, а следующие два - ширину и высоту изображения, а не координаты правого нижнего угла. Например:
MediaPlayerl.DispiayRect := Rect(10,10,300,200);
Данная строка задает область вывода с координатами левого верхнего угла (10, 10) шириной изображения 300 и высотой 200 точек.
Свойство DragMode определяет поведение компонента в процессе его перетаскивания. Данное свойство может принимать одно из двух значений: automatic и dmManual. В первом случае, от разработчика не требуется обработка событий. Достаточно щелкнуть на компоненте и начать его перетаскивание. Во втором случае, компонент не может начать процесс перетаскивания, пока приложение не вызовет метод BeginDrag.
Свойство Drawingstyle применяется для задания стиля изображения. Данное свойство может принимать следующие значения:
- dsFocused - изображение на 25% смешивается с цветом, указанным в свойстве BlendColor. Данное значение влияет только на те изображения, которые содержат маску;
- deselected - то же, что и dsFocused, только смешивание происходит на 50%;
- dsNormal - изображение рисуется цветом, указанным в свойстве BkColor. Если BkCoior равно ciNone, то изображение рисуется прозрачным с использованием маски;
- dsTransparent - изображение рисуется с помощью маски, независимо от свойства BkCoior.
Свойство Enabled показывает, будет ли компонент доступен пользователю во время работы приложения. Недоступный компонент (Enabled = false) отображается серым цветом. Он игнорирует события клавиатуры, мыши и таймера. Данное свойство применяется для временного ограничения доступа пользователя к компонентам. Например, если мы хотим сделать временно недоступной кнопку, нужно в свойстве Enabled кнопки установить значение false:
Buttonl.Enabled := false;
Свойство Font предназначено для установки шрифта, а также всех его атрибутов (размера, стиля, цвета и т. д.).
Свойство Groupindex применяется для описания способа объединения меню.
Свойство Handle используется для обеспечения доступа к дескриптору окна при обращении к функциям Win32 API, которые требуют дескриптор окна. Свойство Handle применяется только для чтения.

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

Свойство Height применяется для задания высоты компонента.
Свойство HelpContext определяет номер, используемый в контекстно-зависимой справке. То есть задается номер страницы справки, которая будет отображаться на экране, когда пользователь нажмет клавишу .
Свойство Hint применяется для задания текста всплывающей подсказки. Оно обычно состоит из двух частей, разделяемых символом вертикальной черты (|). Например:
Buttonl.Hint := "Открыть[Выбор и открытие файла рисунка"
В данном случае, при наведении указателя мыши на кнопку Buttonl будет выдана всплывающая подсказка "Открыть", вторая часть подсказки будет выведена в специальную область, отведенную для таких подсказок, например, панель состояния. Разработчик может пользоваться только первой частью подсказки, без использования символа (|). Кроме того, для отображения всплывающих подсказок нужно дополнительно установить свойство ShowHint В true.
Свойство imageindex применяется для указания индекса изображения, появляющегося левее надписи данного раздела меню. Индексы начинают нумерацию с нуля.
Свойство lsControl определяет способ сохранения формы. Если оно равно true, то сохраняются только те свойства, которые характерны для элементов управления. Если же оно равно false, то все свойства записываются вместе с формой.
Свойство items применяется для доступа к массиву указателей, хранящихся в объекте типа TList. Свойство items имеет тип Pointer. Индексы начинают нумерацию с нуля.
Свойство Left используется для задания координаты левого края компонента. Для всех компонентов за начало координат берется левый верхний угол клиентской области родительского компонента. Для форм началом координат является левый верхний угол экрана.
Свойство List предназначено для доступа к массиву указателей объекта типа TList. Данное свойство имеет доступ только для чтения.
Свойство LockCount указывает, сколько раз была заблокирована область вывода изображения (канва) компонента для предотвращения эффектов наложения от действий различных потоков многопоточного приложения. Данное свойство доступно только для чтения.
Свойство Mode предназначено для задания режима переноса данных из одной таблицы в другую. В табл. 2.6 приведены возможные значения этого свойства.
Таблица 2.6. Возможные значения свойства Mode
Значение свойства

Режим переноса данных

batAppend

Записи из источника данных переносятся в приемник, не изменяя уже существующих в приемнике данных. Таблица-приемник должна существовать до начала переноса данных

batUpdate

Записи из источника данных переносятся в приемник, изменяя уже существующие значения в приемнике. Новые записи в таблицу-приемник не добавляются. Таблица-приемник должна существовать до начала переноса данных

batAppendUpdate

Записи из источника данных переносятся в приемник, заменяя уже существующие и добавляя новые записи. Таблица-приемник должна быть создана до начала переноса данных и индексирована

batDelete

Записи в таблице-приемнике, которым есть соответствие в таблице-источнике, удаляются из приемника. Таблица-приемник должна быть создана до начала переноса данных и должна быть индексирована

batCopy

Создается копия таблицы-источника. Если до копирования таблица-приемник уже существовала, ее содержимое полностью изменяется на содержимое источника


Свойство Name определяет имя компонента, по которому на него ссылаются другие компоненты, а также которое в дальнейшем используется разработчиком. Имя задается в процессе разработки программы и не должно изменяться во время работы программы. Delphi по умолчанию самостоятельно дает имена новым компонентам, например Button1, Button2, Labell и т. д. Рекомендуется изменять эти имена на более осмысленные.
Свойство Notify указывает, должно ли генерироваться событие onNotify после завершения вызова некоторых методов медиаплейера (open, close, Next и др.).
Свойство Parent определяет родительский компонент (родительский компонент - это не родительский объект!) контейнерного типа, в пределах которого располагается данный компонент.

Примечание
Важно различать два похожих свойства. Свойство Parent определяет родительский компонент, т. е. тот компонент, на котором располагается данный компонент. Свойство Owner определяет владельца компонента. Владелец - это компонент, который передается в качестве параметра в конструктор компонента и который владеет им. Например, форма является владельцем всех расположенных на ней компонентов (и, одновременно, родителем).

Свойство Parentctiso предназначено для определения, будет ли компонент наследовать от своего родительского компонента значение свойства ctiso.
Свойство ParentFont предназначено для определения, будет ли для данного компонента использоваться шрифт, применяемый в родительском компоненте контейнерного типа. Например, если на форме находится кнопка, то при установке у этой кнопки свойства parentFont в true приведет к тому, что надпись на кнопке будет выполнена тем шрифтом, который указан в свойстве Font формы.
Свойство ParentshowHint предназначено для включения и выключения родительского свойства showHint. Данное свойство используется для одновременного разрешения или запрещения показа всплывающих подсказок в пределах некоторой формы или компонента контейнерного типа.
Свойство Реп определяет атрибуты пера, которое используется для рисования линий. Данное свойство имеет тип треп. Свойство Реп определяет такие атрибуты, как ширину, цвет, стиль линий и режим рисования пером.
Свойство PenPos предназначено для определения положения пера на области для рисования. Значение этого свойства можно изменять вызовом метода MoveTo и другими методами. Можно непосредственно установить требуемое значение данного свойства, это будет равносильно вызову метода MoveTo.
Свойство Pitch применяется для установки ширины символов шрифта. Применяемые в Windows шрифты могут быть двух типов:
- шрифты с фиксированной шириной символа, т. е. каждый символ имеет определенную ширину, равную ширине других символов;
- шрифты с различной шириной символов. Рассмотрим возможные значения свойства Pitch:
- fpDefauit - ширина шрифта устанавливается равной значению, принимаемому по умолчанию, т. е. такой, какая описана для данного шрифта;
- fpFixed - одинаковая ширина для всех символов; - fpvariable - различная ширина символов.
Свойство Pixels предназначено для получения значения цвета точки с координатами х и Y в пределах текущей области компонента, определенной свойством clipRect. Попытка получить значение цвета точки вне области, заданной свойством ciipRect, возвращает значение -1.
Свойство shortcut предназначено для задания комбинации горячих клавиш, с помощью которых пользователь может выбрать нужный раздел меню.
Свойство showHint применяется для включения или выключения показа всплывающих подсказок при задержке указателя мыши над компонентом. Текст подсказки задается в свойстве Hint.
Свойство showing предназначено для определения, видит ли пользователь данный компонент в настоящий момент времени. При этом не учитывается, что этот компонент могут загораживать другие компоненты. Данное свойство предназначено только для чтения. Свойство showing связано со свойством visible. Если свойство visible компонента и всех его родителей (компонентов, содержащих его) равно true, то и showing равно true. Если же свойство visible компонента или какого-то из его родителей равно false, то Showing равно false.
Свойство size определяет размер используемого шрифта.
Свойство Taborder применяется для задания последовательности перехода от одного компонента формы к другому при помощи клавиши <Таb>. Нумерация осуществляется начиная с нуля. Если задать свойству Taborder компонента значение -1, то этот компонент не сможет получить фокус при помощи клавиши <Таb>. Предположим, что у нас на форме расположены три кнопки: Button1, Button2 и Button3. Для того чтобы фокус между этими кнопками передавался следующим образом: сначала активна кнопка Button2, потом Button1 и затем Button3, нужно установить свойство Taborder для кнопки Button1 равным 1, для Button2 равным 0, а для Button3 равным 2.
Свойство Tabstop применяется для указания возможности передачи фокуса на, компонент при помощи клавиши <Таb>. Если значение этого свойства равно true, то при помощи клавиши <Таb> можно передать фокус данному компоненту, в зависимости от его свойства Taborder. Если значение свойства Tabstop равно false, то независимо от свойства Taborder фокус будет невозможно передать при помощи клавиши <Таb>.
Свойство Tag применяется программистом по своему усмотрению. Тип данного свойства Longint. Разработчик может использовать свойство Tag в своих целях, помещая в него нужную информацию.
Свойство Text предназначено для задания или чтения строки текста, связанной с данным компонентом. Применяется в компонентах типа TEdit и в компонентах-списках.
Свойство TextFlags применяется для задания способа вывода текста в область вывода при помощи методов TextOut и TextRect. При задании способа вывода можно воспользоваться следующими константами:
- ETO_CLIPPED - выводится текст, размещающийся в заданной прямоугольной области;
- ETO_OPAQUE - выводится текст с непрозрачным цветом фона;
- ETO_RTLREADING - текст выводится справа налево. Доступно только для версий Windows, предназначенных для восточных стран;
- ETO_GLYPH_INDEX - текст передается непосредственно в GDI Windows. Применяется только для шрифтов TrueType.
Свойство тор предназначено для задания или чтения значения, определяющего координату верхнего края компонента.
Свойство Transparentcoior определяет прозрачный цвет при рисовании.
Свойство visible применяется для определения, будет ли виден компонент на форме во время выполнения программы. Если свойство имеет значение true, то компонент будет виден, иначе - невидим.
Свойство width используется для установки горизонтального размера компонента, а также для чтения текущего значения ширины компонента.

Свойства компонента

Свойства компонента предназначены для доступа пользователя к внутренним полям компонента. Свойства позволяют изменять атрибуты компонента, в том числе и вычисляемыми значениями. Определение свойства компонента должно содержать его имя и тип, а также как минимум одно объявление способа доступа к данному свойству (описания). Синтаксис определения свойства компонента имеет вид:
property Имя свойства: тип описания;
- имя свойства - должно быть уникальным;
- тип - определяет, значения какого типа могут быть записаны в данном свойстве;
- описания - объявления способов доступа к этому свойству.
Как мы уже говорили, каждое свойство компонента должно иметь как минимум одно обязательное объявление способа доступа к свойству. К таким описаниям относятся read или write. Кроме того, в Delphi имеются дополнительные описания: stored, default (nodefault), implements.
Рассмотрим описания более подробно.
Синтаксис обязательных описаний read и write имеет следующий вид:
read поле или метод write поле или метод
где поле или метод - поле, из которого берется (или в которое помещается) значение свойства, или метод, который берет или помещает значение свойства компонента.
Несложно догадаться, что объявление read (читать) описывает поле или метод, предназначенный для чтения значения свойства, а объявление write (писать) описывает поле или метод, предназначенный для записи значения свойства компонента.
К необязательным описаниям способа доступа относятся описания stored, default (nodefault), implements.
Эти описания называют хранимыми описаниями, они не дают эффекта при выполнении программы, но отвечают за способы хранения переменных средой Delphi.
Описание stored может принимать любое из двух значений: true или false. Оно используется, когда Delphi сохраняет текущее состояние компонента. При сохранении, Delphi проверяет свойства компонента, если свойство отличается от величины, установленной по умолчанию и описание stored имеет значение true, то в этом случае новое значение величины свойства компонента сохраняется. В противном случае (когда описание stored имеет значение false) новое значение величины свойства не сохраняется.
Для установки значения свойства компонента по умолчанию используется писание default.
Приведем пример описания свойства компонента:
property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
В этом примере мы описываем свойство компонента, которое имеет имя MaxLength, тип integer и получает значение из поля FMaxLength, а для записи значения в поле FMaxLength используется метод SetMaxLength. По умолчанию, значение данного свойства равно о.
Наконец, описание implements служит для поддержки свойством компонента интерфейсов. Например,
property Mylnterface: IMylnterface read FMylnterface implements IMylnterface;
Описание implements должно находиться в самом конце описаний. Оно может содержать несколько интерфейсов, разделенных запятыми. Свойство, содержащее описание implements, должно:
- быть классом или интерфейсом;
- не быть массивом;
- иметь описание read.

Типы компонентов

В Delphi имеется четыре базовых типа компонентов. Вы можете использовать в своих приложениях любые компоненты данных типов, а также создавать новые компоненты любого из них.
Первый тип компонентов - стандартный.
Стандартные компоненты включают в себя поведение элементов управления Windows. К таким компонентам относятся: TListView, TRichEdit, TImageList и др. Все эти компоненты находятся на вкладке Win32 панели компонентов Delphi. Каждый из данных компонентов представляет собой так называемую оболочку (wrapper) стандартного элемента управления Windows. Эти компоненты позволяют легко использовать элементы управления Windows в ваших программах.
Второй тип компонентов - пользовательский.
К пользовательским компонентам относятся все компоненты, которые невходят в стандартную библиотеку компонентов Delphi. Эти компоненты добавляются вами в существующий набор компонентов. Пользовательские компоненты создаются вами самостоятельно или приобретаются у других программистов.

Примечание
Информацию о создании собственных компонентов см. в главе 6.

Третий тип компонентов - графический.
Графические компоненты предназначены для создания визуальных элементов управления, которые не могут получать фокус ввода. Обычно, подобные компоненты применяются для отображения какой-либо информации (текстовой или графической). В качестве примера таких компонентов можно Привести TShape, TLabel, TImage И Др.
И, наконец, последний тип компонентов - невизуальный.
Невизуальные компоненты не видны пользователю во" время выполнения приложения. Хотя они обладают свойствами, событиями и методами. Ярким примером компонента такого типа является TTimer.

Типы свойств

Следует отметить, что к свойствам применимы те же правила, которые используются для описания типов в Object Pascal. Типы свойств компонента нужны в первую очередь для отображения и редактирования его в окне инспектора объектов. Перечислим основные типы свойств компонентов Delphi (табл. 2.1).
Таблица 2.1. Типы свойств
Тип свойства

Отображение свойств в окне инспектора объектов

Простой

(Simple)

Числовые, символьные и строковые свойства отображаются соответственно как числа, символы и строки. Разработчик может редактировать значения данных свойств непосредственно в окне инспектора объектов

Перечисляемый

(Enumerated)

Свойства перечисляемых типов (включая булевы) отображаются в инспекторе объектов как редактируемые строки. Разработчик может кроме непосредственного ввода значения свойства переключать допустимые значения свойства двойным щелчком в поле значения свойства инспектора объектов, либо выбирать допустимое значение из выпадающего списка

Множество

(Set)

Свойства типа Set отображаются в инспекторе объектов как множества. При помощи двойного щелчка на значении свойства разработчик может расширить множество и установить каждому элементу множества булево значение (true, если данный элемент входит во множество)

Объект (object)

Свойства такого типа часто имеют свой собственный редактор свойств. Свойства типа объект должны быть потомками класса TPersistent

Массив (Array)

Свойства типа Array должны иметь свой собственный редактор свойств. Инспектор объектов Delphi не поддерживает редактирование свойств данного типа. Разработчик должен при регистрации компонента определить редактор свойств



Типы времени выполнения (RTTI)

Информация о типе во время выполнения (Runtime Type Information, RTTI) - это способность среды Delphi предоставлять приложению информацию об объектах во время выполнения приложения. Так как все объекты Delphi являются потомками класса TObject, то все они содержат указатель на информацию о типе, а также методы для работы с этой информацией. В приведенной ниже табл. 2.8 приведены некоторые из этих методов.
Таблица 2.8. Методы для работы с RTTI
Название метода

Тип возвращаемого результата

Результат

Classlnfo

Pointer

Указатель на информацию о типе объекта

ClassName

String

Имя класса объекта

ClassParent

Tclass

Тип класса-предка объекта

ClassType

Tclass

Тип объекта

InheritsFrom

Boolean

Является ли объект потомком данного класса

InstanceSize

Word

Размер объекта, как экземпляра класса, в байтах


Для работы с информацией о типе времени исполнения в Delphi имеется два основных оператора: is и as. Первый применяется для сравнения типов, второй - для преобразования типов времени выполнения.
Информация о типе времени выполнения используется в Delphi в основном для определения, с объектом какого типа работает сейчас приложение. Рассмотрим пример использования информации RTTI. Разместим на новой форме несколько различных компонентов. Предположим, что в какой-то момент во время работы приложения нам необходимо отключить все кнопки, расположенные на форме. Для реализации этого можно пройтись по всем компонентам формы, узнать их тип, и если это тип TButton, то задать свойству Enabled данного компонента значение false. Код для этого примера приведен на листинге 2.5.

Листинг 2.5
for i:=0 to ComponentCount-1 do
if Components[i] is TButton then
TButton(Components[i]).Enabled:=false;

В данном примере мы используем оператор is для определения типа компонента. Теперь рассмотрим пример использования оператора as. Код, представленный на листинге 2.6, отключает все компоненты, которые произошли от одного предка TControi.

Листинг 2.6
for i:=0 to ComponentCount-1 do
(Sender as TControi).Enabled:=false;

Глава 5 Содержание Глава 7

ActiveX в Delphi

Абстрактные методы

Абстрактными могут быть виртуальные или динамические методы. Абстрактными методами называются такие методы, которые описаны внутри определения класса или компонента, но не содержат никаких действий и никогда не вызываются. Такие методы используются только в компонентах или классах-предках. Описание абстрактного метода выглядит так:
procedure MyProcedure; virtual; abstract;

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

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

Глава 6 Содержание Глава 8

Динамические методы

Динамические методы похожи на виртуальные, они также могут быть перегружены. Основная разница между виртуальными и динамическими методами - в способе их вызова. Если для виртуальных методов строится таблица виртуальных методов, то каждому динамическому методу присваивается уникальное число-идентификатор, после чего строится таблица динамических методов (Dynamic Method Table), в которую заносится данное число, а также адрес метода. Еще одно отличие динамических методов от виртуальных заключается в том, что таблицы динамических методов содержат методы только одного компонента или класса (не включая его предков). Поэтому существенно экономится память, но замедляется время работы, т. к. для поиска адреса метода обычно приходится просматривать несколько таблиц динамических методов.
Описание динамического метода может выглядеть так:
type
TComponent = class
procedure MyProcedure; dynamic;
end;

Методы-сообщения

Методы-сообщения не вызываются из программы непосредственно, как другие методы. Этот тип методов предназначен для того, чтобы выполнить какие-либо действия в ответ на сообщение Windows. В качестве примера рассмотрим описание метода-сообщения:
type
TComponent = class
procedure MyProcedure(Var A: TMessage); message wm_MessageWindows;
end;
После служебного слова message ставится значение (в нашем случае wmjfessagewindows), определяющее сообщение Windows, в ответ на которое будет вызванан метод MyProcedure.
Виртуальные и динамические методы могут быть замещенными (overriden) или абстрактными (abstract).

Основы создания компонентов

Итак, мы приступаем к процессу создания собственного визуального или невизуального компонента. Для создания собственного компонента важно иметь представление о библиотеке визуальных компонентов Delphi, об иерархии компонентов. Все это мы уже рассматривали выше. Для чего же нужны новые компоненты? Ответ неоднозначный. Решение о создании но вых компонентов может быть принято по ряду причин, среди которых:
- разработка нового пользовательского интерфейса, с дальнейшим использованием его в других приложениях;
- создание принципиально нового класса, которого нет в стандартной библиотеке Delphi и среди элементов ActiveX;
- упрощение кода приложения, путем введения новых компонентов; - распространение своих компонентов среди других программистов;
- и, наконец, желание глубоко изучить Delphi, разобраться с тонкостями программирования.
Естественно, кроме упомянутых причин, вы можете назвать множество собственных. Создание компонентов по сложности практически не отличается от разрабокти приложений. Конечно, все зависит от сложности компонента. Но если вы уже решились на сотворение компонента, рекомендации будут следующими:
- определите для себя, какие действия должен выполнять компонент; - разработайте краткий алгоритм, по которому будет работать компонент;
- разбейте всю конструкцию компонента на независимые части;
- предоставьте возможность дальнейшей разработки компонента (возможно, в будущем вы захотите создать на его основе компонент-потомок);
- напишите код компонента (этот пункт разбивается на такие этапы):
выбор предка для вашего компонента;
создание заготовки (модуля) компонента;
создание свойств, событий и методов компонента;
отладка и тестирование;
регистрация компонента в среде Delphi;
создание справки для вашего компонента. Далее мы рассмотрим перечисленные выше этапы создания компонента.

Создание заготовки компонента

Итак, вы выбрали класс-предок для вашего компонента. Теперь можно приступать к созданию модуля вашего компонента. Создание модуля (заготовки) для нового компонента можно выполнить путем вызова окна Delphi, которое называется экспертом компонентов (Component Expert). Данное окно можно вызвать путем выбора в главном меню Delphi пункта Component/New Component (Компонент/Новый компонент). При этом появляется окно, изображенное на рис. 2.13.

Создание заготовки компонента

Рис. 2.13. Окно эксперта компонентов
Рассмотрим данное окно подробнее. Итак, поле ввода Ancestor type предназначено для ввода класса-предка для нового компонента. Это поле ввода содержит в выпадающем списке все зарегистрированные классы библиотеки VCL. Предположим, что мы будем создавать компонент, предком которого является кнопка TButton. Для этого выберем в выпадающем списке класс TButton. Поле Class Name предназначено для ввода имени нового класса. Пусть в нашем случае это будет новый класс TMyButton. Заметьте, что по умолчанию Delphi заполняет это поле именем класса-предка с добавлением порядкового номера (в нашем случае TButtoni). Еще одно поле Palette Page показывает, на какой вкладке палитры компонентов будет расположен новый компонент после его регистрации. Оставим в этом поле значение, предлагаемое Delphi по умолчанию samples. Два следующих поля Unit file name и Search path заполняются средой Delphi самостоятельно, но разработчик может их изменить. Мы не будем этого делать в нашем примере. В результате окно эксперта компонентов должно быть заполнено так, как показано на рис. 2.14.

Создание заготовки компонента

Рис. 2.14. Заполненное окно эксперта компонентов
После заполнения полей данного окна нажимаем кнопку ОК, и Delphi автоматически создаст заготовку модуля вашего компонента. Модуль заготовки для нашего примера представлен на листинге 2.7.

Листинг 2.7
unit MyButton;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls; type
TMyButton = class(TButton)
private
{ Private declarations }
protected
{ Protected declarations } public
{ Public declarations } published
{ Published declarations } end;
procedure Register; implementation procedure Register; begin
RegisterComponents('Samples', [TMyButton]); end;
end.

Итак, заготовка для нового компонента готова. Она не содержит никаких новых свойств, методов и событий для создаваемого компонента. Нужно отметить, что данный компонент уже имеет все свойства, события и методы, которые определены для класса TButton. Мы уже кратко рассматривали процедуру создания нового класса (см. раздел "Объектно-ориентированное программирование" главы 1). Там же мы обсуждали основные типы свойств класса. Обратим теперь взор на основные типы методов компонента.
Все методы могут быть одного из нескольких типов: статические (static), виртуальные (virtual), динамические (dynamic) или методы-сообщения (message). По умолчанию, методу присваивается статический тип.

Статические методы

Статические методы аналогичны обычным функциям или процедурам Delphi. Адрес такого метода известен среде Delphi на стадии компиляции, поэтому Delphi производит статический вызов метода во время выполнения программы. Статические методы работают быстрее всех других методов, но не могут быть перегружены (overload).
Перегрузка метода подразумевает, что класс или компонент может содержать несколько методов с одинаковым именем, но разными списками параметров.
Статический метод может быть описан так:
type
TComponent = class
procedure MyProcedure;
end;
Здесь, метод MyProcedure является статическим. Мы опустили в данном примере название базового класса TObject после слова class. По умолчанию, компилятор создает наследников класса TObject.

Виртуальные методы

Виртуальные методы, в отличие от статических, поддерживают перегрузку, поэтому вызов таких методов для среды Delphi намного сложнее (заранее неизвестен адрес конкретного вызываемого метода). Для того чтобы решить эту проблему, Delphi строит таблицу виртуальных методов (Virtual Method Table), благодаря которой компилятор может определить адрес метода во время выполнения программы. Эта таблица содержит виртуальные методы не только самого класса или компонента, но и его предков. Естественно, хранение такой таблицы увеличивает расходы памяти, но вызов виртуальных методов выполняется быстрее, чем вызов динамических методов.
Описание виртуального метода выглядит следующим образом:
type
TComponent = class
procedure MyProcedure; virtual;
end;
В данном случае метод MyProcedure - виртуальный.

Выбор предка компонента

Итак, вы уже знаете основные классы, имеющиеся в VCL Delphi. Ранее мы рассмотрели базовые классы, которые могут являться предками вашего компонента (см. главу 6). Эти классы перечислены в табл. 2.9.
Таблица 2.9. Базовые классы VCL
Класс

Возможности класса

TObject

Классы, предком которых является данный класс, не являются компонентами. Класс TObject применяется при создании объектов, которые, обычно, являются предками для других компонентов

TComponent

Применяется для создания невизуальных компонентов

TGraphicControl

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

TWinControl

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

TCustomControl

Этот класс является потомком TWinControl и дополняет его областью вывода (канвой). В данный класс добавлен метод Paint. Рекомендуется использовать настоящий класс для создания пользовательских оконных компонентов

TCustomClassName

Библиотека визуальных компонентов содержит несколько классов, у которых не все свойства объявлены как published, т. е. доступные из других модулей, но на основе данных классов можно создавать классы-потомки, в которых и объявлять данные свойства. Таким образом, разработчик может создать несколько идентичных классов на основе одного класса ClassName и в каждом из этих классов определять необходимые свойства из набора предопределенных свойств

TComponentName

Позволяет создавать компоненты-потомки, предками которых являются обычные компоненты или классы VCL Delphi. Таким образом, если перед разработчиком стоит задача расширить возможности какого-либо компонента Delphi, можно использовать данный класс


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

Замещенные методы

Замещение методов предполагает передачу и изменение методов от компонента (класса) предка компоненту (классу) наследнику. Как мы уже отметили, только виртуальные или динамические методы могут быть замещенными. Рассмотрим пример:
type
TComponentChild = class (TComponentParent)
procedure MyProcVirtual; override;
procedure MyProcDynamic; override;
end;
Применение служебного слова override после названия метода позволяет заместить оригинал метода компонента предка методом компонента наследника. При этом замещение происходит непосредственно в таблице виртуальных методов (или таблице динамических методов). При использовании
Служебных слов virtual или dynamic вместо override, произойдет просто создание нового метода вместо замещения старого.
Замещение методов не работает со статическими методами - при замещении статического метода новым произойдет простая замена метода родителя в потомке.

ActiveX в Delphi

Команды Default и NoDefault

Итак, мы уже умеем создавать свойства произвольного типа для собственного компонента. Осталось заметить, что многим свойствам можно присвоить конкретное значение, которое будет установлено по умолчанию. Для этого достаточно присвоить это значение полю компонента, например:
FMyProperty := 10;
В результате чего, при каждом добавлении компонента на форму свойство Myproperty будет принимать значение 10.
Команды Default и NoDefault применяются для ускорения процесса загрузки формы при работе приложения. Например,
property MyCount: Integer read FMyCount write FmyCount Default 0;
Данный код не присваивает значение о свойству MyCount. При выполнении вышеприведенного кода команда Default о означает следующее: если при сохранении формы, содержащей компонент, значение свойства MyCount не будет равно нулю, то новое значение сохранится в файле формы, иначе значение данного свойства не будет сохранено.

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

Команда NoDefault предназначена для нейтрализации команды Default. Команда применяется для отмены команды Default компонентов-предков. Пример использования команды NoDefault:
TSecondComponent = class (TMyButton)
published
property MyCount NoDefault 0;

Пример создания нового события компонента

Попробуем теперь создать собственное событие. Для этого нужно сначала убедиться, что такого события нет в VCL Delphi. Предположим, возникла необходимость создания события, которое возникает каждые 30 секунд. Естественно, для этого случая можно воспользоваться компонентом Timer, который расположен на вкладке System палитры компонентов Delphi. Но, предположим, что наш компонент должен иметь такое событие для удобства работы с ним. Код для создания события представлен на листинге 2.20.

Листинг 2.20
unit halfmin; interface
uses
Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms,
Dialogs,
ExtCtrls;
type
TTimeEvent = procedure (Sender: TObj'ect; TheTime: TDateTime) of object;
THalfMinute = class (TComponent)
private
FTimer: TTimer;
FOnHalfMinute: TTimeEvent;
FOldSecond, FSecond: Word;
procedure FTimerTimer (Sender: TObject);
protected
procedure DoHalfMinute (TheTime: TDateTime); dynamic;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property OnHalfMinute: TTimeEvent read FOnHalfMinute write FOnHalf-Minute;
end;

implementation
constructor THalfMinute.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
if not (csDesigning in ComponentState) then begin
FTimer:=TTimer.Create(self);
FTimer.Enabled:=True;
FTimer.Interval:=500;
FTimer.OnTimer:=FTimerTimer;
end;
end;

destructor THalfMinute.Destroy;
begin
FTimer.Free;
inherited Destroy;
end;

procedure THalfMinute.FTimerTimer (Sender: TObject);
var
DT: TDateTime;
Temp: Word;
begin
DT:=Now;
FOldSecond:=FSecond;
DecodeTime (DT,Temp,Temp,FSecond,Temp);
if FSecond <> FOldSecond then
if ((FSecond=30) or (FSecond=0)) then
DoHalfMinute (DT);
end;

procedure THalfMinute.DoHalfMinute(TheTime: TDateTime);
begin
if Assigned (FOnHalfMinute) then
FOnHalfMinute (Self, TheTime);
end;
end.

Для проверки работоспособности вышеприведенного кода вы можете добавить еще одну процедуру для регистрации нового компонента с именем ThalfMinute, предварительно добавив в interface-часть программы строку:
procedure Register;
Ниже представлен код для регистрации компонента:
procedure Register;
begin
RegisterComponents('Samples', [THalfMinute]);
end;
Для просмотра работоспособности нового компонента после его регистрации создадим новую форму и разместим на ней новый компонент. Добавим на форму компонент TEdit. Затем добавим обработчик события onHaifMinute для формы (листинг 2.21).

Листинг 2.21
procedure TForml.HalfMinutelHalfMinute(Sender: TObject; TheTime: TDateTime);
begin
Editl.Text: = ('Время '+TimeToStr(TheTime)) ; Editl. Refresh;
end;

В результате работы данной программы в компоненте Editl будет выводиться текущее время каждые 30 секунд.

Регистрация компонента в среде Delphi

Регистрация компонента необходима для размещения компонента в палитре компонентов.
При использовании эксперта компонентов для создания нового компонента Delphi самостоятельно создает процедуру регистрации компонента в модуле-заготовке. Создателю компонента в данном случае ничего не нужно более делать, кроме выполнения следующих шагов:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля (естественно, предварительно нужно сохранить модуль компонента).
После компиляции на выбранной вкладке палитры компонентов появится новый компонент.
Если же вы создаете компонент без использования эксперта компонентов, вам придется самостоятельно дописывать процедуру регистрации компонента. В разделе interface модуля компонента нужно дописать строку:
procedure Register;
А в разделе implementation добавить процедуру регистрации, например:
procedure Register;
begin
RegisterComponent ('Samples', [TMyComponent]);
end;
В результате, компонент с именем TMyComponent будет размещен на вкладке samples палитры компонентов.
Обратите внимание на значок, которым обозначается новый компонент. Его можно поменять на любой другой. Для создания собственного значка разумно воспользоваться стандартной программой Image Editor, которая входит в комплект поставки Delphi. Можно использовать и любой другой редактор растровых изображений.
Создайте значок для вашего компонента размером 24x24 пиксела. Данное изображение сохраните в файле формата DCR. Имя файла - это имя вашего компонента, в котором все буквы - заглавные. Например, для компонента TMyButton имя файла картинки будет TMYBUTTON.DCR. Затем поместите файл картинки в ту папку, в которой находится файл с модулем компонента. Перекомпилируйте модуль, и ваш компонент будет изображаться в палитре компонентов вашим рисунком.

Глава 7 Содержание Глава 9

Создание методов компонента

Добавление методов в новый компонент - операция несложная. Однако нужно обратить внимание на некоторые особенности, которые в дальнейшем облегчат взаимодействие пользователя с вашим компонентом.
Во-первых, необходимо, чтобы методы не были взаимозависимыми, т. е. каждый метод должен быть самостоятельным и законченным. Во-вторых, метод не должен блокировать компонент. И, в-третьих, метод должен иметь имя, соответствующее выполняемым действиям.
Как вы уже знаете, методы объявляются в секциях private, public и protected. При создании нового метода важно учитывать, как он будет использоваться в дальнейшем, сможет ли данный компонент быть предком для других компонентов, и, в зависимости от этого, разместить методы в нужных секциях. Табл. 2.10 поможет вам при выборе секций для методов компонента.
Таблица 2.10. Размещение методов компонента в различных секциях
Секция

Размещаемые методы

Private

В данной секции лучше всего размещать те методы, которые не могут изменяться в компонентах-потомках. Эти методы не доступны вне данного компонента

Protected

В этой секции размещают методы, которые будут доступны для изменения в компонентах-потомках

Public

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

Published

В этой секции размещаются свойства компонента, которые доступны во время разработки приложения в окне инспектора объектов



Создание перечисляемых свойств компонента

К свойствам перечисляемого типа относятся такие свойства компонента, которые при их редактировании в окне инспектора объектов вызывают выпадающий список, содержащий возможные значения данного свойства. К числу подобных свойств относятся Align, Borderstyle, color и др. Для того чтобы самостоятельно добавить в новый компонент перечисляемое свойство, необходимо сначала определить новый перечисляемый тип, например:
TMyEnumType = (eFirst, eSecond, eThird);
После этого нужно определить поле компонента, которое будет хранить значение данного перечисляемого типа, затем определить свойство. Пример добавления перечисляемого типа в новый компонент TMyButton приведен на листинге 2.9.

Листинг 2.9
type
TMyEnumType = (eFirst, eSecond, eThird);
type
TMyButton = class(TButton)
private
{ Private declarations }
FMyEnum: TMyEnumType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
end;

Таким образом, в окне инспектора объектов, при изменении свойства MyEnumProp, будет выдан выпадающий СПИСОК, Содержащий Три пункта: eFirst, eSecond И eThird (рис. 2.15).

Создание перечисляемых свойств компонента

Рис. 2.15. Перечисляемое свойство MyEnumProp в новом компоненте TMyButton

Создание собственных редакторов свойств

Перед тем как создавать собственный редактор свойств компонента, рассмотрим сначала имеющиеся в Delphi редакторы свойств. Их достаточно легко видеть в окне инспектора объектов, когда вы пытаетесь изменить какое-либо свойство компонента. Например, когда вы изменяете свойство Color для того или иного компонента, вы видите редактор свойства цвета. Важно заметить, что все свойства компонентов имеют свои редакторы, даже такие простейшие свойства, как Caption, Height, Enabled. Особенностью этих редакторов является то, что компоненты "не знают", какие редакторы вы используете для изменения их свойств. Это может навести на мысль взять собственный редактор свойств вместо предопределенного. Например, можно написать редактор свойства width, который будет ограничен каким-либо числом.
Редакторы свойств имеют свою иерархию. Рассмотрим ее.
Базовым классом в иерархии редакторов свойств является TPropertyEditor. Названия классов говорят сами за себя. Например, класс TColorProperty отвечает за свойство цвета компонента, класс TlntegerProperty связан с теми свойствами, которые имеют тип integer и т. д. На листинге 2.15 приведен код, определяющий базовый класс TPropertyEditor.

Листинг 2.15
TPropertyEditor = class
private
FDesigner: TFormDesigner;
FPropList: PInstPropList;
FPropCount: Integer;
constructor Create(ADesigner: TFormDesigner; APropCount: Integer);
function GetPrivateDirectory: string;
procedure SetPropEntry(Index: Integer; AInstance: TComponent;
APropInfo: PPropInfo};
protected
function GetPropInfо: PPropInfo;
function GetFloatValue: Extended;
function GetFloatValueAt(Index: Integer): Extended;
function GetMethodValue: TMethod;
function GetMethodValueAt(Index: Integer): TMethod;
function GetOrdValue: Longint;
function GetOrdValueAt(Index: Integer): Longint;
function GetStrValue: string;
function GetStrValueAt(Index: Integer): string;
procedure Modified;
procedure SetFloatValue(Value: Extended);
procedure SetMethodValue(const Value: TMethod);
procedure SetOrdValue(Value: Longint);
procedure SetStrValue(const Value: string);
public
destructor Destroy; override;
procedure Activate; virtual;
function AllEqual: Boolean; virtual;
procedure Edit; virtual;
function GetAttributes: TPropertyAttributes; virtual;
function GetComponent(Index: Integer): TComponent;
function GetEditLimit: Integer; virtual;
function GetName: string; virtual;
procedure GetProperties(Proc: TGetPropEditProc);virtual;
function GetPropType: PTypelnfo;
function GetValue: string; virtual;
procedure GetValues(Proc: TGetStrProc); virtual;
procedure Initialize; virtual;
procedure SetValue(const Value: string); virtual;
property Designer: TFormDesigner read FDesigner;
property PrivateDirectory: string read GetPrivateDirectory;
property PropCount: Integer read FPropCount;
property Value: string read GetValue write SetValue;
end;

Методы данного класса, которые приведены ниже, можно переопределять для изменения поведения редактора свойств.
Метод Activate вызывается, когда данное свойство выбирается в окне инспектора объектов.
Метод AllEqual вызывается при выборе на форме более одного компонента.
Метод Edit вызывается при нажатии кнопки (...) или при двойном щелчке мыши на свойстве. Данный метод может вызвать диалоговое окно для редактирования свойства, например, как это происходит при редактировании свойства Font.
Метод GetAttributes возвращает множество значений типа TProperyAttributes, определяющих, каким образом свойство будет отображаться в окне инспектора объектов.
Метод Getcomponent предназначен для определения компонента по его номеру (параметр index), в случае, когда на форме выбрано несколько компонентов одновременно.
Метод GetEditLimit возвращает максимальное число символов в строке, которые пользователь может ввести при редактировании свойства.
Метод GetName предназначен для получения имени свойства. Данный метод целесообразно изменять только в том случае, когда имя свойства отличается от имени, отображаемом в окне инспектора объектов.
Метод GetPropType применяется для определения указателя на информацию о типе редактируемого свойства.
Метод Getvalue возвращает значение свойства в виде строки.
Метод initialize вызывается при создании (инициализации) редактора свойств.
Метод setvalue применяется для установки значения свойства.
В большинстве случаев, при создании нового редактора свойств нет необходимости использовать в качестве класса-предка базовый класс TPropertyEditor. Часто разработчик просто переделывает уже существующий для данного свойства редактор, переопределяя некоторые его методы.
Рассмотрим в качестве примера переработанное свойство Hint, которое применяется для показа всплывающей подсказки при задержании указателя мыши над компонентом. В стандартном случае такая подсказка имеет всего одну строку. Попробуем сделать свойство Hint многострочным. Нижеприведенный листинг 2.16 показывает, как создать новый редактор свойств THintProperty. В качестве класса-предка для данного редактора свойств выберем редактор TStringProperty.

Листинг 2.16
THintProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
function GetValue : String; override;
procedure Edit; override;
end;

function THintProperty.GetAttributes: TPropertyAttributes;
begin
Result := inherited GetAttributes + [paDialog, paReadOnly];
end;

function THintProperty.GetValue : string;
var i : Byte;
begin
result:=inherited GetValue;
for i:=l to Byte(resultt0]) do
if result[i]<#32 then result[i]: = '>';
end;

procedure THintProperty.Edit; var
HintEditDlg : TStrEditDlg; s : string;
begin
HintEditDlg:=TStrEditDlg.Create(Application);
with HintEditDlg do
try
Memo.MaxLength := 254;
s:=GetStrValue+#0;
Memo.Lines.SetText(@s[1]);
UpdateStatus(nil);
ActiveControl := Memo;
If ShowModal = mrOk then begin
s:=StrPas(Memo.Lines.GetText);
if s[0]>#2 then Dec(Byte(s[0]),2);
SetStrValue(s);
end; finally
Free;
end;
end;

Рассмотрим методы нового класса:
- функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (при этом появляется кнопка (...) в окне инспектора объектов) и paReadOnly (который применяется для того, чтобы редактирование данного свойства было возможно только через диалог);
- функция Getvalue заменяет символы перевода каретки (#10) -и переход на новую строку (#13) на символ больше (>).
Наконец, процедура Edit применяется для вызова диалога для ввода строк всплывающей подсказки.
Для регистрации нового редактора нужно в интерфейсной части модуля поместить объявление процедуры Register. После чего В части implementation модуля написать саму процедуру регистрации (листинг 2.17).

Листинг 2.17.
procedure Register;
begin
RegisterPropertyEditor (Typelnfо(String), TControl, 'Hint', THintProperty);
end;

Данная процедура позволяет привязать один и тот же редактор к свойствам, в зависимости от их названия или типа. Это определяется параметрами, которые передаются процедуре RegisterPropertyEditor. Первый параметр определяет тип свойства (в нашем примере - это string), второй параметр - класс компонента. Третий параметр позволяет задать имя свойства. Четвертый параметр указывает имя редактора свойства.
Для того чтобы установить новый редактор свойств в Delphi, нужно выполнить следующие шаги:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля.
После того как произойдет компиляция библиотеки, можно создать новую форму и разместить на ней какой-либо компонент. После чего установим у этого компонента свойство showHint в true и нажмем кнопку (...) в свойстве Hint. Вы видите на экране новый многострочный редактор для свойства Hint.

Создание событий компонента

Стандартные события мы с вами уже рассматривали в предыдущих главах. В настоящий момент нам предстоит дать четкое определение событию, а также обработчику события.
Итак, событие - это любое действие, произошедшее благодаря операционной системе, действиям пользователя, работе программы.
Событие можно "перехватить" и обработать с помощью программы-обработчика события. Связь между событием и программой-обработчиком называется свойством-событием. Таким образом, когда происходит какое-либо событие компонента, он может обработать данное событие. Для этого сначала происходит проверка наличия кода обработки события. Если подобный код есть - он выполняется.
Рассмотрим в качестве примера такое часто возникающее событие, как нажатие левой кнопки мыши Onclick. Данное событие, как и многие другие, имеет так называемые методы диспетчеризации событий (event-dispatching methods). Эти методы нужны как раз для того, чтобы определять, создан ли код обработки произошедшего события для данного компонента. Эти методы объявляются как защищенные (protected). Таким образом, для свойства Onciick определен метод диспетчеризации события click (листинг 2.18).

Листинг 2.18
TControl = class (TComponent)
private
FOnClick: TNotifyEvent;
protected
procedure Click; dynamic;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;

implementation
procedure TControl.Click;
begin
if Assigned (FOnClick) then FOnClick (Self);
end;
Листинг 2.19
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, '('+IntToStr(X)+', '+IntToStr(Y)+')');
end;

Создание событий компонента

Рис. 2.18. Результат обработки события OnMouseDown

Создание свойств компонента

Добавление новых свойств в компонент осуществляется очень просто. Достаточно задать поля и свойства, определив при этом их тип и доступ (чтение, запись). Пример простого задания свойств в новом компоненте представлен на листинге 2.8.

Листинг 2.8
TMyButton = class(TButton)
private
{ Private declarations }
FMyCount: Integer;
FStirngOfText: String;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyCount: Integer read FMyCount write FMyCount;
property StringOfText: String read FStringOfText write FStringOfText;
end;

На приведенном выше листинге мы задаем два новых поля для компонента TMyButton и определяем два новых свойства компонента: одно типа integer, другое string. Для простоты, значения данных свойств считываются и записываются в одноименные поля. Задание этих свойств будет гарантировать доступ к ним в окне инспектора объектов (благодаря описанию свойств в разделе published) и не потребует написания дополнительных методов для доступа к свойствам.

Примечание
По взаимной договоренности принято названия полей начинать с буквы F (поле, field), а названия компонентов и любых объектов с буквы т (тип, type).


Создание свойств-множеств в компоненте

Тип множества часто фигурировал в Object Pascal, и некоторые свойства компонентов Delphi имеют данный тип. Когда вы используете свойство типа set, вы должны учитывать, что каждый элемент множества будет являться отдельным свойством, имеющим логический тип в инспекторе объектов.
Для создания свойства-множества сначала зададим нужный тип:
TMySetTypeFirst = (poFirst, poSecond, poThird); TMySetType = set of TMySetTypeFirst;
Первая строка задает перечисляемый тип TMySetTypeFirst, который определяет диапазон множества, а вторая строка - само множество TMySetType.
Пример добавления свойства-множества в компонент TMyButton приведен на листинге 2.10.

Листинг 2.10.
type
TMyEnumType = (eFirst, eSecond, eThird); TMySetTypeFirst = (poFirst, poSecond, poThird); TMySetType = set of TMySetTypeFirst;
type
TMyButton = class(TButton)
private
{ Private declarations } FMyEnum: TMyEnumType; FMyOptions: TMySetType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
property MyOptions: TMySetType read FMyOptions write FMyOptions;
end;

Для удобства, мы не стали исключать определение перечисляемого свойства в компоненте TMyButton.
В результате, в окне инспектора объектов свойство-множество будет отображаться, как представлено на рис. 2.16.

Создание свойств-множеств в компоненте

Рис. 2.16. Свойство-множество MyOptions в новом компоненте TMyButton

Создание свойства-массива в компоненте

Свойства компонента могут быть практически любого типа, которые поддерживает язык Object Pascal. Некоторые свойства могут быть массивами. Яркими примерами свойств такого типа являются тмето. Lines, TDBGrid. columns и др. Подобные свойства требуют собственных редакторов. Мы пока остановимся на создании простого свойства, которое представляет из себя массив (о создании собственных редакторов свойств читайте далее в этой главе). Создадим новый компонент Tweek, содержащий два свойства: Month и Number. Свойство Month будет представлять собой массив, возвращающий название месяца по переданному целому числу от 1 до 12. Свойство Number - тоже массив, который возвращает число, соответствующее названию месяца.
Итак, на листинге 2.14 приведен код компонента TWeek.

Листинг 2.14
unit Week; interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TWeek = class(TComponent)
private
{ Private declarations }
function GetMonthName (const AIndex: Integer): String;
function GetMonthNumber (const AMonthNarae: String): Integer;
protected
{ Protected declarations }
public
{ Public declarations }
property MonthName[const AIndex: Integer]: String read GetMonthName; default;
property MonthNumber[const AMonthName: String]: Integer read GetMonthNumber;
published
{ Published declarations }
end;

procedure Register;
implementation
const
MonthNames: array[1..12] of String[8]= ('Январь','Февраль','Март','Апрель','Май', 'Июнь','Июль','Август','Сентябрь', 'Октябрь', 'Ноябрь','Декабрь');

function TWeek.GetMonthName(const AIndex: Integer): String;
begin
if (AIndex<=0) or (AIndex>12) then
raise Exception.Create('Номер месяца должен быть числом от 1 до 12')
else Result:- MonthNames[AIndex];
end;

function TWeek.GetMonthNumber(const AMonthName: String): Integer;
var i:integer;
begin
Result:=0;
for i:=l to 12 do begin
if Uppercase(AMonthName)=UpperCase(MonthNames[i]) then
Result:=1;
end;
end;
procedure Register;
begin
RegisterComponents('Samples', [TWeek]);
end;
end.

Рассмотрим вышеприведенный код более подробно. Как вы можете видеть, свойства типа Array объявляются вместе с индексами. Для свойства MontnName мы определили индекс Aindex, а для свойства MonthNumber - индекс AMonthName. Для доступа к свойствам такого типа необходимо использовать методы. Внутренних полей здесь нет. Если свойство типа Array многомерно, то свойство-массив объявляется сразу с несколькими индексами. При вызове методов доступа к ним нужно передавать параметры в том же порядке, в каком вы их указали в свойстве.
Функция GetMonthName возвращает строку, содержащую имя месяца, соответствующего целому числу от 1 до 12, переданному в качестве параметра данной функции. В случае передачи функции числа, не принадлежащему данному диапазону, будет сгенерировано исключение командой raise (об исключениях читайте главу 2).
Наконец, функция GetMonthNumber возвращает число от 1 до 12, которое соответствует названию месяца, переданного в качестве параметра в данную функцию. В случае если ни один месяц не соответствует названию, определенному массивом MonthNames, результатом выполнения данной функции будет ноль.
Таким образом, если поместить на форму экземпляр компонента TWeek с именем weeki, при выполнении строки ShowMessage (Weekl.MonthName[5]}; будет выдано окно с сообщением Май.

Создание свойства-объекта в компоненте

Каждый компонент может содержать в себе свойство-объект. В качестве свойства-объекта может выступать любой компонент или объект Delphi. Кроме того, свойствами-объектами нового компонента могут быть новые компоненты или объекты, которые вы создали самостоятельно. Важным условием является тот факт, что свойства-объекты должны быть потомками класса TPersistent. Это необходимо для того, чтобы свойства объекта-свойства отображались в окне инспектора объектов. Приведем пример создания свойства-объекта в нашем компоненте TMyButton.
Для начала создадим произвольный новый объект, являющийся прямым потомком класса TPersistent (листинг 2.11).

Листинг 2.11
type
TMyObject = class (TPersistent}
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;

В качестве предка нового класса может выступать не только класс TPersistent, но любой его потомок. В вышеприведенном листинге мы создаем новый класс TMyObject, в котором присутствует два простых свойства - Propertyl и Property2. Кроме того, в новый объект включена процедура Assign. Данная процедура необходима для обеспечения правильного доступа к свойству нашего будущего компонента TMyButton. Ниже приведен листинг 2.12, в котором мы добавляем в компонент TMyButton новое свойство-объект.

Листинг 2.12
type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;

Как вы можете видеть, мы добавляем в код нового компонента конструктор и деструктор объекта.
Теперь осталось дописать конструктор и деструктор для компонента TMyButton, в которых необходимо соответственно создать и уничтожить, кроме компонента TMyButton, еще и объект TMyObject. А также нужно написать код метода setMyObject, который будет помещать новое значение в свойства объекта TMyObject. Ну и, конечно, написать код метода Assign для объекта TMyObject. Полную версию кода представляет листинг 2.13. Здесь, кроме ранее описанного, приводится код ко всем этим методам.

Листинг 2.13
type
TMyObject = class (TPersistent)
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;

type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;

procedure Register;
implementation
procedure TMyButton.SetMyObject (Value: TMyObject);
begin
if Assigned (Value) then FMyObject.Assign (Value);
end;

constructor TMyButton.Create (AOwner; TComponent);
begin
inherited Create (AOwner);
FMyOb j ect:=TMyObject.Create;
end;

destructor TMybutton.Destroy;
begin
FMyObject.Free;
inherited Destroy;
end;

procedure TMyObject.Assign (Source: TPersistent);
begin
if Source is TMyObject then
begin
FPropertyl:=TmyObject (Source).Property1;
FProperty2:=TmyObject (Source).Property2;
inherited Assign (Source);
end;
end;

Обратите внимание на реализацию метода TMyObject.Assign. Здесь сначала выполняется проверка на то, что передается правильный экземпляр объекта TMyObject. Если он правильный, то значения свойств (Source) Переносятся в поля FPropertyl и FProperty2 объекта TMyButton. Результатом исполнения вышеприведенного кода будет новый компонент TMyButton, содержащий в себе свойство-объект TMyObject. В окне инспектора объектов это будет выглядеть, как представлено на рис. 2.17.

Создание свойства-объекта в компоненте

Рис. 2.17. Свойство-объект MyObject в новом компоненте TMyButton

ActiveX в Delphi

Библиотеки типов

СОМ-объекты часто используют библиотеки типов. Библиотека типов - это специальный файл, который содержит информацию о СОМ-объекте. Данная информация содержит список свойств, методов, интерфейсов, структур и других элементов, которые содержатся в СОМ-объекте. Библиотека типов содержит также информацию о типах данных каждого свойства и Типах данных, возвращаемых методами СОМ-объекта.
Файлы библиотеки типов имеют расширение TLB.

Фабрика класса

СОМ-объекты представляют собой экземпляры coclass. Напомним, что Coclass - это класс, поддерживающий один или более интерфейс. СОМ-объекты могут предоставлять только те услуги, которые определены в интерфейсах coclass. Экземпляры Cociass создаются при помощи специального типа объекта, называемого фабрикой класса.
Фабрика класса - это специальный СОМ-объект, который поддерживает интерфейс IclassFactory и отвечает за создание экземпляров того класса, с которым ассоциирована данная фабрика класса.
Интерфейс IclassFactory определен в модуле Delphi ActiveX так:
type
IClassFactory = interface (IUnknown)
['{00000001-0000-0000-COOO-000000000046}']
function Createlnstance (const unkOuter: lUnknown; const iid: TIID out obj): HResult; stdcall;
function LockServer (fLock: BOOL): HResult; stdcall;
end;
Как видно из вышеприведенного куска кода, интерфейс имеет два метода:
Createlnstance и LockServer.
Метод Createlnstance создает экземпляр СОМ-объекта ассоциированной фабрики класса.
Метод LockServer применяется для хранения СОМ-сервера в памяти. Если параметр метода fLock имеет значение true, то счетчик ссылок сервера увеличивается, иначе - уменьшается. Когда счетчик достигает значения о, сервер выгружается из памяти.
Всякий раз, когда услуги СОМ-объекта запрашиваются клиентом, фабрика класса создает и регистрирует экземпляр объекта для конкретного пользователя. Если услуга того же СОМ-объекта запрашивает другой клиент, фабрика класса создает второй экземпляр объекта для обслуживания второго клиента. coclass должен иметь фабрику класса и идентификатор класса CLSID. Использование CLSID для cociass подразумевает, что они могут быть откорректированы всякий раз, когда в класс вводятся новые интерфейсы. Таким образом, в отличие от DLL, новые интерфейсы могут изменять или добавлять методы, не влияя на старые версии.
Мастер создания СОМ-объектов Delphi самостоятельно заботится о создании фабрики класса.

Локальные и удаленные серверы

С использованием СОМ клиент не должен беспокоиться о том, где располагается объект, он просто делает вызов интерфейса данного объекта. Технология СОМ обеспечивает все необходимые шаги для того, чтобы сделать этот вызов. Шаги могут отличаться, в зависимости от местонахождения объекта. Объект может находиться в том же процессе, где и клиент, в другом процессе на том же компьютере, где расположен клиент, или на другом компьютере в сети. В зависимости от этого применяются разные типы серверов:
- внутренний сервер (In-process server);
- локальный сервер или сервер вне процесса (Local server, Out-of-process server);
- удаленный сервер (Remote server).
Внутренний сервер - это библиотека DLL, которая запущена в одном процессе вместе с клиентом. Например, элемент управления ActiveX, который внедрен на Web-страницу и просматривается при помощи Internet Explorer или Netscape Navigator. В данном случае элемент управления ActiveX загружен на клиентскую машину и находится в том же процессе, что и обозреватель Web. Приложение-клиент связывается с сервером внутри процесса при помощи прямых вызовов СОМ-интерфейса. На рис. 3.3. представлена схема взаимодействия клиента с внутренним сервером.

Локальные и удаленные серверы

Рис. 3.3. Схема взаимодействия клиента с внутренним сервером
Внутренний СОМ-сервер должен экспортировать четыре функции:
function DllRegisterServer: HResult; stdcall;
function DllUnregisterServer: HResult; stdcall;
function DllGetClassObject (const CLSID, IID: TGUID; var Obj): HResult;
stdcall;
function DllCanUnloadNow: HResult; stdcall;
Все вышеперечисленные функции уже реализованы в модуле comserv, их нужно только добавить в описания exports вашего проекта.
Рассмотрим данные функции более подробно:
- DllRegisterServer - применяется для регистрации DLL СОМ-сервера в системном реестре Windows. При регистрации СОМ-класса в системном реестре создается раздел в HKEY_CZASSES_ROOT\CLSID\{XXXXXXXX-XXXX-XXXX-xxxx-xxxxxxxx}, где число, записанное вместо символов х, представляет собой CLSID данного СОМ-класса. Для внутреннего сервера в данном разделе создается дополнительный подраздел inProcserver32. В этом подразделе указывается путь к DLL внутреннего сервера (рис. 3.4).
- DllUnregisterServer - применяется для удаления всех разделов, подразделов и параметров, которые были созданы в системном реестре функцией DllRegisterServer при регистрации DLL СОМ-сервера.
- DllGetclassObject - возвращает фабрику класса для конкретного СОМ-класса.
- DllcanUnloadNow - применяется для определения, можно ли в настоящий момент времени выгрузить DLL СОМ-сервера из памяти. Функция проверяет, есть ли указатели на любой СОМ-объект данной DLL, если есть, то возвращает значение S_FALSE, т. е. DLL выгрузить нельзя. Если ни один СОМ-объект данной DLL не используется, то функция возвращает значение SJTRUE.

Локальные и удаленные серверы

Рис. 3.4. Путь к локальному СОМ-серверу в окне редактора системного реестра
Локальный сервер - это приложение ЕХЕ, которое запущено в другом процессе, но на одном компьютере вместе с клиентом. Например, лист электронной таблицы Microsoft Excel связан с документом Microsoft Word. При этом два разных приложения работают на одном компьютере. Локальные серверы используют СОМ для соединения с клиентом.
Когда клиент и сервер находятся в различных приложениях, а также, когда они находятся на разных компьютерах в сети, СОМ использует внутренний (внутрипроцессный) прокси (In-process proxy) для реализации процедуры удаленного вызова. Прокси располагается в одном процессе вместе с клиентом, поэтому, с точки зрения клиента, вызов интерфейсов осуществляется так же, как и в случае, когда клиент и сер'вер находятся внутри одного процесса. Задача прокси заключается в том, чтобы перехватывать вызовы клиента и перенаправлять их туда, где запущен сервер. Механизм, который позволяет клиенту получать доступ к объектам, расположенным в другом адресном пространстве или на другом компьютере, называется маршалинг (marshaling).
Функции маршалинга:
- принимать указатель интерфейса из процесса сервера и делать указатель прокси в процессе клиента доступным;
- передавать аргументы вызовов интерфейса таким образом, как будто они произошли от клиента и размещать аргументы в процесс удаленного объекта.
Для любого вызова интерфейса клиент помещает аргументы в стек, вызывает необходимую функцию СОМ-объекта через указатель интерфейса. Если вызов объекта произошел не внутри процесса, вызов проходит через прокси. Прокси упаковывает аргументы в пакет маршалинга и передает получившуюся структуру удаленному объекту. Заглушка (stub) объекта распаковывает пакет маршалинга, выбирает аргументы из стека и вызывает необходимую функцию СОМ-объекта.
Таким образом, маршалинг - это процесс упаковки информации, а демаршалинг - процесс распаковки информации.
Тип маршалинга зависит от объектной принадлежности СОМ. Объекты могут использовать стандартный механизм маршалинга, предоставляемый интерфейсом IDispatch. Стандартный маршалинг позволяет устанавливать связь при помощи стандартного системного удаленного вызова процедуры (Remote Procedure Call, RFC).
На рис. 3.5 изображена схема, показывающая методику взаимодействия клиента и сервера в случае, когда приложения работают на одном компьютере, но в разных приложениях.

Локальные и удаленные серверы

Рис. 3.5. Схема взаимодействия клиента с сервером в разных процессах на одном компьютере
Локальный СОМ-сервер регистрируется в системном реестре Windows так же, как и внутренний СОМ-сервер.
Удаленный сервер - это библиотека DLL или иное приложение, запущенное на другом компьютере. То есть клиент и сервер работают на разных компьютерах в сети. Например, приложение базы данных, написанное с помощью Delphi, соединяется с сервером на другом компьютере в сети. Удаленный сервер использует распределенные СОМ-интерфейсы (Distributed COM, DCOM) для связи с клиентом.
Удаленный сервер работает также с помощью прокси. Различие в работе между локальным и удаленным сервером заключается в типе используемой межпроцессной связи. В случае локального сервера - это СОМ, а в случае удаленного сервера - DCOM. Схема взаимодействия клиента и удаленного сервера показана на рис. 3.6.

Локальные и удаленные серверы

Рис. 3.6. Схема взаимодействия клиента с сервером на разных компьютерах

OLE-объекты

Часть данных, использующаяся совместно несколькими приложениями, называется OLE-объектом. Те приложения, которые могут содержать в себе OLE-объекты, называются OLE-контейнерами (OLE container). Приложения, имеющие возможность содержать свои данные в OLE-контейнерах, называются OLE-серверами (OLE server).

Основной СОМ-интерфейс IUnknown

Базовый интерфейс lunknown достаточно подробно был рассмотрен во второй главе книги. В дополнение ко всему вышесказанному, добавим, что интерфейс lunknown обеспечивает механизм учета ссылок (счетчик ссылок на СОМ-объект). При передаче указателя на интерфейс выполняется метод интерфейса lunknown AddRef. По завершении работы с интерфейсом приложение-клиент вызывает метод Release, который уменьшает счетчик ссылок.
При вызове метода Querylnterface интерфейса Iunknown в метод передается параметр IID, имеющий тип TGUID, т. е. идентификатор интерфейса. Параметр метода out возвращает либо ссылку на запрашиваемый интерфейс, либо значение NH. Результатом вызова метода может быть одно из значений, перечисленных в табл. 3.1.
Таблица 3.1. Значения, возвращаемые методом Queryinterface
Значение

Описание

S_OK

Интерфейс поддерживается

E_NOINTERFACE

Интерфейс не поддерживается

E_UNEXPECTED

Неизвестная ошибка



Пользователь СОМ-объекта

Пользователем СОМ-объекта называется приложение или часть приложения, которое использует СОМ-объект и его интерфейсы в своих собственных целях. Как правило, СОМ-объект находится в другом приложении.

Расширения СОМ

Технология СОМ изначально разрабатывалась как ядро для осуществления межпрограммного взаимодействия. Уже на этапе разработки предполагалось расширять возможности технологии при помощи так называемых расширений СОМ. СОМ расширяет собственную функциональность, благодаря созданию специализированных наборов интерфейсов для решения конкретных задач.
Технология ActiveX - это технология, которая использует компоненты СОМ, особенно элементы управления. Она была создана для того, чтобы работа с элементами управления была более эффективной. Это особенно необходимо при работе с приложениями Internet/Intranet, в которых элементы управления должны быть загружены на компьютер клиента, прежде чем они будут использоваться.
Технология ActiveX - не единственное расширение СОМ. В табл. 3.2 представлены некоторые из используемых в настоящее время расширений СОМ.
Перечисленные в табл. 3.2 расширения СОМ - это далеко не все из имеющихся. Постоянно идет доработка старых и создание новых, более совершенных технологий межпрограммного взаимодействия.
Таблица 3.2. Список расширений СОМ
Расширение СОМ

Краткое описание

Серверы автоматизации (Automation servers)

Серверы автоматизации- это объекты, которыми можно управлять из других приложений во время работы приложения. Таким образом, автоматизация- это способность приложения программно контролировать объекты других приложений

Диспетчеры автоматизации или СОМ-клиенты (Automation Controllers, COM Clients)

Диспетчеры автоматизации- это клиенты серверов автоматизации. Они позволяют разработчику или пользователю писать сценарии для управления серверами автоматизации

Элементы управления ActiveX (ActiveX Controls)

Элементы управления ActiveX предназначены для серверов внутри процесса (in-process COM servers). Элементы ActiveX обычно используются путем встраивания в приложение-клиент

Библиотеки типов (Type Libraries)

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

Страницы активного сервера (Active Server Pages)

Активные серверные страницы- это компоненты ActiveX, которые позволяют вам создавать динамически изменяющиеся Web-страницы

Активные документы (Active Documents)

Активные документы - это объекты, которые поддерживают связывание и внедрение, визуальное редактирование, перенос (drag-and-drop). В качестве примера таких документов можно представить документы Microsoft Word и книги Microsoft Excel

Визуальные межпроцессные объекты (Visual Cross-process Objects)

Визуальные межпроцессные объекты- это визуальные объекты, которыми можно управлять из других процессов


На рис. 3.7 представлена диаграмма, которая показывает связь некоторых расширений СОМ и их связь с технологией СОМ.
Использование СОМ-объектов имеет как преимущества, так и некоторые ограничения. СОМ-объекты могут быть как визуальными, так и невизуальными. Какие-то СОМ-объекты должны быть запущены в одном процессе с клиентом, другие - в разных процессах либо на разных,,компьютерах.
Приведенная ниже табл. 3.3 кратко описывает особенности объектов каждого из вышеприведенных расширений СОМ.

Расширения СОМ

Рис. 3.7. Технологии, основанные на СОМ
Таблица 3.3. Особенности объектов СОМ
СОМ-объект

Визуаль-ность

Процесс

Связь

Библиотека типов

Активный документ (Active Document)

Обычно визуальный

Внутренний или локальный

OLE

Нет

Автоматизация (Automation)

Может быть как визуальным, так и невизуальным

Внутренний, локальный или удаленный

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

IDispatch

Требуется для автоматического маршалмнга

Элемент управления ActiveX (ActiveX Control)

Обычно визуальный

Внутренний

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

IDispatch

Требуется

Произвольный объект интерфейса

По выбору

Внутренний

Не требуется маршалинг

Рекомендуется

Произвольный объект интерфейса

По выбору

Внутренний, локальный или удаленный

Автоматический маршалинг в зависимости от библиотеки типов, в противном случае-ручной маршалинг

Рекомендуется



Глава 8 Содержание Глава 10

Развитие СОМ-технологий

Одной из важнейших задач, которые ставила перед собой фирма Microsoft, когда продвигала операционную систему Windows, была задача по обеспечению эффективного взаимодействия между различными программами, работающими в Windows.
Самыми первыми попытками решить эту непростую задачу были буфер обмена, разделяемые файлы и технология динамического обмена данными (Dynamic Data Exchange, DDE).
После чего была разработана технология связывания и внедрения объектов (Object Linking and Embedding, OLE). Первоначальная версия OLE 1 предназначалась для создания составных документов. Эта версия была признана несовершенной и на смену ей пришла версия OLE 2. Новая версия позволяла решить вопросы предоставления друг другу различными программами собственных функций. Данная технология активно внедрялась до 1996 года, после чего ей на смену пришла технология ActiveX, которая включает в себя автоматизацию (OLE-автоматизацию), контейнеры, управляющие элементы, Web-технологию и т. д.

Счетчики ссылок

Каждый СОМ-объект имеет счетчик ссылок. Данный счетчик содержит число процессов, которые в текущий момент времени используют СОМ-объект. Под процессом здесь подразумевается любое приложение или DLL, которые используют СОМ-объект, т. е. пользователи СОМ-объекта. Счетчик ссылок на СОМ-объект нужен для того, чтобы высвобождать процессорное время и оперативную память, занимаемую СОМ-объектом, в том случае, когда он не используется.
После создания и обращения к СОМ-объекту счетчик ссылок увеличивается на единицу. Всякий раз, когда новое приложение подключается к СОМ-объекту - счетчик увеличивается. Когда процесс отключается от СОМ-объекта - счетчик уменьшается. При достижении счетчиком нуля память, занимаемая СОМ-объектом, высвобождается.

СОМ-интерфейс

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

СОМ-интерфейс

Рис. 3.1. СОМ-интерфейс

Примечание
По правилам обозначения СОМ-объектов, интерфейсы СОМ-объекта обозначаются кружками справа или слева от СОМ-объекта. Базовый интерфейс lUnknown рисуется кружком сверху от СОМ-объекта.

Для примера, каждый СОМ-объект всегда поддерживает основной СОМ-интерфейс lUnknown, который применяется для передачи клиенту сведений о поддерживаемых интерфейсах.
Как уже говорилось выше, СОМ-объект может иметь несколько интерфейсов, каждый из которых обеспечивает какую-либо свою функцию.
Ключевыми аспектами СОМ-интерфейсов являются следующие.
- Однажды определенные, интерфейсы не могут быть изменены. Таким образом, вы можете возложить на один интерфейс определенный набор функций. Дополнительную функциональность можно реализовать с помощью дополнительных интерфейсов.
- По взаимному соглашению, все имена интерфейсов начинаются с буквы I, например IPersist, IMalloc.
- Каждый интерфейс гарантированно имеет свой уникальный идентификатор, который называется глобальный уникальный идентификатор (Globally Unique Identifier, GUID). Уникальные идентификаторы интерфейсов называют идентификаторами интерфейсов (Interface Identifiers, IIDs). Данные идентификаторы обеспечивают устранение конфликтов имен различных версий приложения или разных приложений.
- Интерфейсы не зависят от языка программирования. Вы можете воспользоваться любым языком программирования для реализации СОМ-интерфейса. Язык программирования должен поддерживать структуру указателей, а также иметь возможность вызова функции при помощи указателя явно или неявно.
- Интерфейсы не являются самостоятельными объектами, они лишь обеспечивают доступ к объектам. Таким образом, клиенты не могут напрямую обращаться к данным, доступ осуществляется при помощи указателей интерфейсов.
- Все интерфейсы всегда являются потомками базового интерфейса Iunknown.

СОМ-интерфейсы

СОМ-интерфейс применяется для объединения методов СОМ-объекта. Интерфейс позволяет клиенту правильно обратиться к СОМ-объекту, а объекту - правильно ответить клиенту. Названия СОМ-интерфейсов начинаются с буквы I. Клиент может не знать, какие интерфейсы имеются у СОМ-объекта. Для того чтобы получить их список, клиент использует базовый интерфейс lunknown, который есть у каждого СОМ-объекта.

СОМ-классы

СОМ со-классы (coclass) - это классы, которые содержат один или более СОМ-интерфейс. Вы можете не обращаться к СОМ-интерфейсу непосредственно, а получать доступ к СОМ-интерфейсу через со-класс. Со-классы идентифицируются при помощи идентификаторов класса (CLSID).

СОМ-клиенты

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

СОМ-объект

СОМ-объект представляет собой двоичный код, который выполняет какую-либо функцию и имеет один или более интерфейс.
СОМ-объект содержит методы, которые позволяют приложению пользоваться СОМ-объектом. Эти методы доступны благодаря СОМ-интерфейсам. Клиенту достаточно знать несколько базовых интерфейсов СОМ-объекта, чтобы получить полную информацию о составе свойств и методов объекта. СОМ-объект может содержать один или несколько интерфейсов. Для программиста СОМ-объект работает так же, как и класс в Object Pascal.

СОМ-серверы

СОМ-сервер представляет собой приложение или библиотеку, которая предоставляет услуги приложению-клиенту или библиотеке. СОМ-сервер содержит один или более СОМ-объектов, где СОМ-объекты выступают в качестве наборов свойств, методов и интерфейсов.
Клиенты не знают как СОМ-объект выполняет свои действия. СОМ-объект предоставляет свои услуги при помощи интерфейсов., В дополнение, приложению-клиенту не нужно знать, где находится СОМ-объект. Технология СОМ обеспечивает прозрачный доступ независимо от местонахождения СОМ-объекта.
Когда клиент запрашивает услугу от СОМ-объекта, он передает СОМ-объекту идентификатор класса (CLSID). CLSID - всего лишь GUID, который применяется при обращении к СОМ-объекту. После передачи CLSID, СОМ-сервер должен обеспечить так называемую фабрику класса (см. следующий раздел), которая создает экземпляры СОМ-объектов.
В общих чертах, СОМ-сервер должен выполнять следующее:
- регистрировать данные в системном реестре Windows для связывания модуля сервера с идентификатором класса (CLSID);
- предоставлять фабрику СОМ-класса, создающую экземпляры СОМ-объектов;
- обеспечивать механизм, который выгружает из памяти серверы СОМ, которые в текущий момент времени не предоставляют услуг клиентам.

Примечание
Для создания СОМ-объектов и СОМ-серверов Delphi предоставляет мастера, который значительно упрощает процедуру. Об этом читайте следующую главу.


Состав СОМ-приложения

При создании СОМ-приложения необходимо обеспечить следующее:
- СОМ-интерфейс;
- СОМ-сервер;
- СОМ-клиент.
Рассмотрим эти три составляющие СОМ-приложения более подробно.

Составные документы

Документ, включающий в себя один или несколько OLE-объектов, называется составным документом. Приложение, которое может содержаться внутри документа, называется ActiveX-документом (ActiveX document).
Остальные термины, присущие технологии СОМ, мы рассмотрим в следующих разделах данной книги.

Технология DCOM

Технология DCOM (Distributed COM) - это распределенная СОМ-технология. Она применяется для предоставления средств доступа к СОМ-объектам, расположенным на других компьютерах в сети (в том числе и сети Internet).
Операционные системы Windows NT 4 и Windows 98 имеют встроенную поддержку DCOM.

Терминология СОМ

Всякая новая технология приносит с собой новые термины для ее описания.

Указатели СОМ-интерфейса

Указатель интерфейса - это 32-битный указатель на экземпляр объекта, который является, в свою очередь, указателем на реализацию каждого метода интерфейса. Реализация методов доступна через массив указателей на эти методы, который называется vtable. Использование массива vtable похоже на механизм поддержки виртуальных функций в Object Pascal.

Указатели СОМ-интерфейса

Рис. 3.2. Схема работы указателя СОМ-интерфейса
Наглядное представление работы указателей СОМ-интерфейса представлено на рис. 3.2.

ActiveX в Delphi

Мастера для создания СОМ-объектов

Как уже было отмечено выше, Delphi предоставляет разработчику несколько мастеров для упрощения процесса генерации СОМ-объектов. В Delphi имеются мастера для создания следующих объектов:
- простого СОМ-объекта;
- объекта автоматизации;
- элемента управления ActiveX;
- страниц активного сервера;
- форм ActiveX;
- библиотек ActiveX;
- страниц свойств;
- библиотек типов;
- объектов Microsoft Transaction Server (MTS).
Мастер автоматизирует задачи, характерные для создания каждого из вышеперечисленных типов СОМ-объектов. Мастер обеспечивает необходимые интерфейсы для каждого типа объекта. Для простого СОМ-объекта мастер предоставляет один требуемый СОМ-интерфейс lunknown, который устанавливает указатель интерфейса в объекте (рис. 3.8).
Для объекта автоматизации мастер создает два интерфейса: lunknown и IDispatch, последний обеспечивает автоматический маршалинг (рис. 3.9).

Мастера для создания СОМ-объектов

Рис. 3.9. Объект автоматизации

Мастера для создания СОМ-объектов

Рис. 3.10. Элемент управления ActiveX
Для элемента управления ActiveX мастер создает все требуемые для элемента управления интерфейсы, такие как lunknown, IDispatch, IOleobject, Iolecontrol и др. (рис. 3.10). Полный список интерфейсов, используемых элементом управления ActiveX, мы рассмотрим в главах 11 и 12.
Итак, различные мастера предоставляют различные интерфейсы. Вы можете выбрать тот мастер, который реализует необходимые для вашего СОМ-объекта интерфейсы. Табл. 3.4 показывает, какие интерфейсы предоставляют различные мастера.
Таблица 3.4. Интерфейсы, предоставляемые различными мастерами
Мастер

Предоставляемые интерфейсы

Действия, выполняемые мастером

СОМ-сервер

lUnknown

Экспортирует необходимые программы, которые осуществляют регистрацию сервера, регистрацию класса, загрузку и выгрузку сервера и экземпляров объекта. Создает и управляет фабрикой класса для объектов, осуществленных в сервере. Указывает СОМ, какие потоковые модели используются для интерфейсов объекта. Обеспечивает библиотеку типов, если она требуется

Сервер автоматизации

lUnknown, IDispatch

Выполняет все описанное для мастера СОМ-сервера, кроме того, обеспечивает интерфейс пользователя и автоматически обеспечивает библиотеку типов

Элемент управления ActiveX

lUnknown, IDispatch, IPersistStreamlnit, IQlelnPlaceActiveObject, IPer sis t Storage, IViewObject, IQleObject, IViewOb j ect2 , ZOleControl, IPerPropertyBrowsing, IQlelnPlaceObject, IspecifyPropertyPages

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

Активные формы

lUnknown, IDispatch, IPersistStreamlnit, lOlelnPlaceActiveObject, IPersistStorage, IViewObject, IQleObject, IViewOb ject2, IQleControl, IPerPropertyBrowsing, IGlelnPlaceObject, IspecifyPropertyPages

Выполняет все описанное для мастера элемента управления ActiveX, кроме того, позволяет разработчику редактировать приложение

Объект активный сервер

lUnknown, IDispatch

Выполняет все описанное для мастера сервера автоматизации и генерирует страничку с расширением ASP, которая может быть загружена в обозреватель Web. Мастер позволяет разработчику модифицировать свойства и методы объекта при помощи редактора библиотеки типов

Библиотека ActiveX

Нет

Создает новую DLL для ActiveX или СОМ-сервера и обеспечивает все необходимые функции

Страница свойств

lUnknown, I PropertyPage

Создает новую страницу свойств, которую разработчик может редактировать в редакторе форм

Библиотека типов

Нет

Создает новую библиотеку типов и ассоциирует ее с активным проектом

MTS-объект

lobjectControl

Добавляет новый модуль в текущий проект, который содержит объявление MTS-объекта


Если вы хотите добавить собственный СОМ-объект, Delphi предоставляет вам такую возможность. Для создания нового интерфейса вы должны создать потомок интерфейса IDispatch и определить в нем необходимые методы. Для изменения уже существующего интерфейса вам нужно создать его прямого потомка и модифицировать его по своему усмотрению. Таким образом, непосредственное изменение уже существующих интерфейсов для разработчика недоступно.

Проектирование СОМ-объекта

При проектировании СОМ-объекта вы должны четко представлять, какие СОМ-интерфейсы должны быть реализованы. Мастер по умолчанию предоставляет интерфейс lunknown. Кроме того, вы обязаны определить, какой тип сервера вы хотите использовать: внутренний, локальный или удаленный. Если вы проектируете внутренний или локальный СОМ-сервер - вы должны подключить библиотеку типов, при этом маршалинг будет произведен автоматически.

Создание СОМ-объекта с использованием мастера

Далее перечислены шаги, которые нужно выполнить для создания нового СОМ-объекта с помощью мастера:
1. Выберите в главном меню Delphi пункт File/New (Файл/Новый), при этом откроется диалог добавления новых частей проекта (рис. 3.11).
2. Выберите вкладку ActiveX в данном диалоговом окне.
3. Дважды щелкните на иконке, изображающей СОМ-объект.

Создание СОМ-объекта с использованием мастера

Рис. 3.11. Диалоговое окно добавления новых частей проекта
В результате вышеописанных действий, появится окно мастера СОМ-объекта (COM Object Wizard), изображенное на рис. 3.12.

Создание СОМ-объекта с использованием мастера

Рис. 3.12. Мастер СОМ-объекта
Окно мастера СОМ-объекта содержит несколько полей ввода и флажков: - поле Class Name (Имя класса) - определяет имя нового СОМ-объекта;
- поле Instancing (Экземпляры) - определяет способ создания СОМ-объекта, данное поле может принимать любое из трех значений:
Internal (Внутренний) - объект может быть создан только внутри процесса, внешнее приложение не может непосредственно создавать экземпляры данного объекта. Примером такого способа создания СОМ-объекта может служить текстовый процессор, который может содержать в качестве объекта документ, который будет создаваться только путем вызова метода данного текстового процессора.
Single Instance (Одиночный экземпляр) - допускает только один СОМ-интерфейс для каждого приложения. Таким образом, когда какое-нибудь приложение присоединяется к объекту, объект становится недоступным для остальных приложений.
Multiple Instance (Множественный экземпляр) - объект может быть подключен к нескольким приложениям. Всякий раз, когда клиент запрашивает услуги сервера, создается новый экземпляр сервера. Например, каждый раз, когда пользователь пытается открыть Windows Explorer, создается новый экземпляр Проводника.

Примечание
Если СОМ-объект используется только внутри процесса, поле Instancing (Экземпляры) игнорируется.

- поле Threading Model (Потоковая модель) - определяет, каким образом приложения клиенты могут вызывать интерфейсы данного СОМ-объекта;

Примечание
О потоковых моделях читайте далее в этой главе.

- поле Implemented Interfaces (Реализуемые интерфейсы) - определяет СОМ-интерфейсы, которые должны быть реализованы в СОМ-объекте;
- поле Description (Описание) - сюда вы можете поместить описание вашего СОМ-объекта;
- флажок Include Type Library (Включать библиотеку типов) - устанавливается в случае, если вы хотите создавать библиотеку типов для вашего объекта. Установка данного флажка автоматически производит установку другого флажка Mark interface Oleautomation (Отметить интерфейс Oleautomation);
- флажок Mark interface Oleautomation (Отметить интерфейс Oleautomation) - устанавливается для активирования кода маршалинга. СОМ знает как осуществлять маршалинг всех объектов, совместимых с сервером автоматизации, и устанавливает прокси и заглушки автоматически.

Выбор модели потока

При создании нового СОМ-объекта с помощью мастера вам придется выбирать модель потока. Правильный выбор модели потока улучшит работу вашего СОМ-объекта.
Табл. 3.5 кратко описывает модели потоков, возможные для СОМ-объектов.
Таблица 3.5. Модели потоков для СОМ-объектов
Модель потока

Описание

Преимущества и недостатки модели

Single

Без поддержки потоков. Запросы клиента обрабатываются последовательно, один за другим

Клиенты обрабатываются в порядке очереди, поэтому потоковой поддержки не требуется

Apartment

Клиенты могут вызывать методы объекта только из того потока, в котором был создан сам объект. Разные объекты одного сервера могут быть вызваны из разных потоков, но каждый объект может быть вызван только из одного потока

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

Free

Клиенты могут вызывать любые методы объекта в любое время и из любых потоков. Количество потоков неограничено

Нужно защищать все данные экземпляров и глобальные данные объекта. Локальные переменные не надежны из-за многочисленных вызовов. Клиенты просты в написании, но написание кода объектов может вызывать трудности. Первоначально использовалась для реализации технологии DCOM

Both

Объекты могут поддерживать клиентов, которые используют модели Apartment или Free

Максимальная гибкость и улучшение работы


При взаимодействии обе части СОМ-приложения, клиент и сервер, сообщают о поддерживаемой ими потоковой модели. СОМ сравнивает эти модели. Если обе части описывают одну потоковую модель, СОМ устанавливает прямое соединение между клиентом и сервером. Если потоковые модели клиента и сервера различаются, СОМ использует маршалинг для установления связи между частями приложения. Применение маршалинга снижает скорость выполнения работы, но преимущество очевидно: маршалинг обеспечивает совместную работу приложений, использующих разные потоковые модели.
Потоковая модель применима только для серверов внутри процесса. Локальные серверы регистрируются как приложения ЕХЕ и самостоятельно обеспечивают потоковую модель.
В следующих главах мы рассмотрим на конкретных примерах процесс создания СОМ-объектов.

Глава 9 Содержание Глава 11

ActiveX в Delphi

Интерфейс IDispatch

Объекты автоматизации представляют собой СОМ-объекты, которые используют интерфейс IDispatch. Данный интерфейс описан в модуле system следующим образом:
type
IDispatch = interface (IUnknown)
[' {00020400-0000-0000-СООО-000000000046} ' ]
function GetTypelnfoCount(out Count: Integer): Integer; stdcall;
function GetTypelnfo(Index, LocalelD: Integer; out Typelnfo):Integer; stdcall;
function GetlDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocalelD: Integer; "
DispIDs: Pointer): Integer; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocalelD: Integer; Flags: Word;
var Params; VarResult, Exceptlnfo, ArgErr: Pointer): Integer;
end;
Основной функцией интерфейса IDispatch является метод invoke. Приложение-клиент может вызывать данный метод для выполнения определенных действий на сервере автоматизации. Для того чтобы указать, какой именно метод хочет вызвать клиент, он передает при вызове метода invoke параметр Displo. Этот параметр представляет собой число, которое называется идентификатор диспетчера (dispatch ID). Данное число указывает, какой именно метод должен исполняться на сервере. Следующий параметр метода invoke - параметр IID не используется. Параметр LocalelD содержит информацию о локализации. Параметр Flags указывает, как данный метод будет вызываться: для получения свойств, для установки свойств или обычным способом. Свойство Params содержит указатель на массив TDispparams. Данный массив хранит параметры, передаваемые методу. Параметр VarResult представляет собой указатель на olevariant, который содержит возвращаемое значение вызываемого метода (если такое имеется), а параметр Excepinfo - указатель на запись TExcepinfo, которая содержит информацию об ошибке, если метод invoke возвращает значение DISP_E_EXCEPTION. Последний параметр ArgError - это указатель на целое число, которое является индексом некорректного параметра в массиве Params. В данном случае метод invoke возвращает значение DISP_E_TYPEMISMATCH или DISP_E_PARAMNOTFOUND.
Следующий метод интерфейса invoke - метод GetlDsOfNames. Он применяется для получения идентификатора диспетчера, одного или нескольких методов по строкам имен этих методов. Параметр IID данного метода не используется. Параметр Names - это указатель на массив имен методов. Такой массив имеет тип PWideChar. Параметр NameCount содержит число, показывающее количество строк в массиве, на который указывает параметр Names. Параметр LocalelD содержит информацию о локализации. Параметр Displos - это указатель на массив целых чисел NameCount.
Метод GetTypelnfo применяется для получения информации о типе объектов автоматизации. Параметр index описывает получаемую информацию о типе и должен (обычно) быть равен нулю. Параметр LCID содержит ин формацию о локализации. Если метод выполнился успешно, то параметр Typeinfo будет содержать указатель iTypeinfo на информацию о типе объекта автоматизации.
Метод GetTypeinfoCount используется для получения числа интерфейсов информации о типе, поддерживаемых объектом автоматизации. Число возвращается в параметре count. Он может содержать два возможных значения: 0, если объект автоматизации не поддерживает информацию о типе, и 1, если поддерживает.

Обработка событий диспетчера автоматизации

Как только вы инсталлировали необходимый сервер автоматизации в палитру компонентов, вы можете использовать инспектор объектов для написания обработчиков событий. Обработка событий осуществляется так же, как и обработка событий других компонентов Delphi:
1. Поместите на форму сервер автоматизации из палитры компонентов.
2. Щелкните на компоненте, затем нажмите вкладку Events в окне инспектора объектов - вы увидите список событий данного компонента.
3. Дважды щелкните напротив нужного события, после чего Delphi сгенерирует заготовку для обработчика события, в который вы можете поместить свой код.
После того как вы напишете необходимые обработчики событий, вы можете приступать к подключению к серверу автоматизации.

Подключение к серверу автоматизации

Рассмотрим процесс подключения к серверу автоматизации на примере таких программ, как Microsoft Word, Microsoft Excel и Microsoft Outlook, входящих в состав популярного программного пакета Microsoft Office. Подключение к другим серверам автоматизации может немного отличаться, но принцип работы везде одинаков.
Прежде чем переходить непосредственно к подключению к серверу автоматизации, рассмотрим объектную модель Microsoft Office.
В Microsoft Office нет понятия наследование, вместо него используется так называемое встраивание. Встраивание помогает получать новые классы. Итак, в любом приложении Microsoft Office всегда имеется центральный базовый объект. Для Microsoft Word это word.Application, для Microsoft Excel - Excel.Application. Microsoft Outlook сам является базовым объектом и называется outLookAppiication, однако, несмотря на это, в объект Application встраиваются все остальные объекты, которые, в свою очередь, являются свойствами базового объекта.
Различные объекты приложений Microsoft Office имеют самые разнообразные методы, но некоторые из них одинаковы для разных приложений. К числу совпадающих методов относятся Run, Activate и др.

Примечание
Разница в этих методах все же есть. В различных приложениях Microsoft Office они имеют разные параметры. Действия методов тоже могут отличаться.

Рис. 3.14 показывает небольшую часть структуры объекта Microsoft Word Object.Более полную информацию об объектной архитектуре Microsoft Office можно найти в справочных материалах по Microsoft Office.

Подключение к серверу автоматизации

Рис. 3.14. Фрагмент структуры объекта Microsoft Word Object
Как только открывается новый документ, приложения Microsoft Office, автоматически создается каркас нового документа, который представляет собой набор библиотек с классами. Объекты этих классов будут доступны в данном документе. Задачей разработчика диспетчера автоматизации является получить доступ к корневому объекту сервера, выстроить цепочку доступа к встроенным объектам и правильно передать параметры.
Итак, базовым объектом любого приложения Microsoft Office является объект Application. Попробуем получить к нему доступ средствами Delphi.

Примечание
Все написанное ниже, справедливо для пятой (или выше) версии Delphi.

Итак, выполним следующие шаги:
1. Создадим новый проект Delphi с помощью пункта главного меню File/New Application (Файл/Новое приложение).
2. Поместим на форму компонент wordApplication с вкладки Servers палитры компонентов Delphi.
3. Установим свойстваа AutoConnect И AutoQuit компонента WordAppiication
в значение true (эти свойства отвечают за автоматическую загрузку и выгрузку из памяти сервера автоматизации после запуска приложения).
4. Запустим приложение с помощью пункта главного меню Run/Run (Запуск/Запуск).
В результате, хотя визуально ничего не наблюдается, кроме отображения формы, приложение проделало большую работу. Наше приложение запустило сервер автоматизации Microsoft Word. Чтобы убедиться в этом, достаточно посмотреть список задач, выполняемых Windows во время работы нашего приложения. Нажмите комбинацию клавиш ++ во время работы приложения и убедитесь, что среди задач, выполняемых Windows, появилась задача Winword.exe.
В общих чертах, наше приложение выполнило следующее:
- при запуске приложения, в системном реестре Windows был найден сервер автоматизации WordApplication при помощи идентификатора (CLSID);
- запустилось приложение, находящееся по адресу, указанному в системном реестре Windows;
- сервер автоматизации предоставил нашему приложению (простейшему диспетчеру автоматизации) интерфейс, через который приложение может получить доступ к базовому объекту Microsoft Word.
Теперь закройте приложение и посмотрите еще раз на задачи, выполняемые Windows. Сервер автоматизации выгружен из памяти компьютера при помощи интерфейса IDispatch (данный интерфейс имеет метод _ADDRef, при помощи которого можно определить число клиентов, которые в настоящий момент пользуются услугами сервера).
Тот же результат можно получить, если использовать код, приведенный на листинге 3.1. Для этого разместите на форме две кнопки Start и Finish.

Листинг 3.1
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics', Controls, Forms, Dialogs, StdCtrls, COMObj;
type
TForml = class(TForm)
Start: TButton;
Finish: TButton;
procedure StartClickfSender: TObject);
procedure FinishClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Forml: TForml;
wd:01eVariant;
fileName:string;
implementation {$R *.DFM}
procedure TForml.StartClick(Sender: TObject);
begin
try
fileName:=ExtractFilePath(Application.EXEName)+'report.DOC';
// Создаем объект интерфейса для доступа к серверу СОМ
wd:=Create01eObject('Word.Application');
// Проверка наличия методов и правильность передачи параметров будет осуществляться на стадии выполнения приложения
wd.application.documents.add;
wd.application.activedocument.range.insertAfter(now);
wd.application.activedocument.saveas(fileName);
except
end;
end;

procedure TForml.FinishClick(Sender: TObject);
begin
// Выгружаем сервер из памяти компьютера
wd.application.quit(true,0);
end;
end.

Обратите внимание на необходимость добавления модуля coMObj в раздел Uses.
Как вы можете видеть, вкладка Servers палитры компонентов содержит много компонентов. Кроме базовых объектов серверов автоматизации, данная вкладка содержит несколько вложенных объектов, таких как документ Microsoft Word (WordDocument), рабочая Книга Excel (ExcelWorkbook) И Др.
Отметим, что все компоненты, содержащиеся на вкладке Servers, являются наследниками класса TOLEServer, который, в свою очередь, происходит от класса TComponent. Класс TOLEServer - базовый класс для всех СОМ-серверов, которые можно получить путем импортирования библиотек типов серверов автоматизации. Данный класс имеет несколько свойств и методов, позволяющих управлять связью с СОМ-сервером. Например, свойство Autoconnect, которое, в случае, если оно имеет значение true, автоматически запускает СОМ-сервер и производит извлечение интерфейса для связи сервера и диспетчера автоматизации.
Рассмотрим очень важное свойство данного класса ConnectKind (тип подключаемого процесса). Это свойство используется методом Connect, который вызывается автоматически при AutoConnect=true. В табл. 3.6 представлены значения свойства ConnectKind.
Таблица 3.6. Значения, принимаемые свойством ConnectKind
Значения свойства

ConnectKind

CkRunningOrNew

Описание

Диспетчер автоматизации подключается к уже существующему процессу. В случае, если процесс не запущен - запускает и подключается к нему. Этот вид взаимодействия между диспетчером и сервером автоматизации является наиболее часто применяемым. Данное значение свойства устанавливается по умолчанию

CkNewInstance

Диспетчер автоматизации в любом случае создает новый экземпляр сервера автоматизации

CkRunninglnstance

Соединение устанавливается только с уже запущенные сервером автоматизации, если такой, не обнаружен - генерируется ошибка

CkRemote

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

CkAttachToInterface

В данном случае соединение не создается, поэтому нельзя устанавливать значение true для свойства AutoConnect. Соединение с сервером в этом случае осуществляется при помощи метода ConnectTo



Примечание
При помощи последнего значения свойства ConnectKind - CkAttachToInterface- можно последовательно подключать к уже существующему интерфейсу несколько компонентов, таких как wordDocument или WordFont. Установка данного значения свойства необходима, когда диспетчер автоматизации должен отслеживать события, которые происходят в сервере автоматизации.

Свойство ConnectKind можно устанавливать в окне инспектора объектов при помощи выпадающего списка, который появляется при щелчке мышью на данном свойстве.

Позднее и раннее связывание

Объекты автоматизации могут работать двумя способами, которые называются позднее связывание (late binding) и раннее связывание (early binding).
При позднем связывании необходимый метод сервера автоматизации вызывается клиентом внутри метода invoke интерфейса IDispatch. При использовании позднего связывания вызов метода не разрешен до момента его исполнения, а возможен только с помощью метода invoke. Во время компиляции вызов метода сервера автоматизации автоматически преобразуется в вызов метода IDispatch, invoke. Во время выполнения приложения метод invoke вызывает нужный метод сервера автоматизации.
Раннее связывание означает, что сервер автоматизации предоставляет свои методы при помощи пользовательского интерфейса, который является наследником интерфейса IDispatch. В данном случае диспетчер автоматизации может обращаться к методам сервера автоматизации напрямую, без вызова метода invoke интерфейса IDispatch. Использование раннего связывания позволяет ускорить работу объектов автоматизации.
Многие объекты автоматизации поддерживают так называемый двойной интерфейс (dual interface). Это означает, что такие объекты автоматизации позволяют вызывать методы как из метода invoke, так и из потомков интерфейса IDispatch. Серверы автоматизации, которые создаются при помощи Delphi, всегда поддерживают двойной интерфейс. Диспетчеры автоматизации, созданные при помощи Delphi, позволяют вызывать методы напрямую внутри интерфейса либо внутри метода invoke.

Создание диспетчера автоматизации

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

Примечаниe
Импортирование библиотеки типов сервера автоматизации особенно важно для разработчиков, которые используют старые версии Delphi. В Delphi 5 в палитру компонентов была добавлена вкладка Servers (Серверы), на которой располагаются значки, обеспечивающие доступ к основным серверам автоматизации, установленным на данном компьютере.

Для того чтобы импортировать библиотеку типов, нужно выполнить следующее:
1. Выбрать в главном меню Delphi пункт Project/Import Type Library (Проект/Импорт библиотеки типов).
2. В появившемся диалоговом окне (рис. 3.13) выбрать нужную библиотеку типов из представленного списка.

Примечание
В списке диалогового окна импортирования библиотеки типов содержатся все библиотеки типов, зарегистрированные в операционной системе на данном компьютере. Если нужная вам библиотека типов отсутствует в списке, вы можете ее туда добавить при помощи кнопки Add (Добавить). После того как вы нажмете кнопку Add (Добавить), найдите и добавьте нужную библиотеку типов с расширением TLB, OLB, DLL, OCX или EXE. Вы можете также удалить ненужную библиотеку типов из списка диалогового окна при помощи кнопки Remove (Удалить). Просто выделите ненужную библиотеку типов и нажмите кнопку Remove (Удалить).

Создание диспетчера автоматизации

Рис. 3.13. Диалоговое окно импортирования библиотеки типов
3. В выпадающем списке Palette page (Страница палитры компонентов) диалогового окна выберите страницу палитры компонентов Delphi, на которую будет размещен выбранный вами сервер автоматизации.
4. Убедитесь в том, что флажок Generate Component Wrapper (Создать суперобложку компонента) включен.
5. Нажмите кнопку Install (Установить).

Создание сервера автоматизации

Сервером автоматизации может являться либо приложение, либо DLL. Рассмотрим шаги, которые вам предстоит выполнить для создания сервера автоматизации.
1. Создайте приложение или DLL, которое должно выступать в роли сервера автоматизации. Можно использовать любое ранее созданное вами приложение и добавить к нему автоматизацию.
2. При помощи мастера объекта автоматизации создайте объект автоматизации и добавьте его к проекту.
3. Добавьте свойства и методы к вашему объекту автоматизации при помощи библиотеки типов. Данные свойства и методы нужны для того, чтобы диспетчеры автоматизации могли их использовать при обращении к вашему серверу автоматизации.
4. Зарегистрируйте приложение как сервер автоматизации. Теперь рассмотрим эти шаги более подробно.
Создадим самое простое приложение, состоящее из одной только формы. Для этого достаточно лишь запустить Delphi и выбрать пункт меню File/New Application (Файл/Новое приложение).
Теперь добавим к нашему проекту объект автоматизации для того, чтобы приложение выполняло функции сервера автоматизации. Чтобы сделать это, нужно выбрать пункт главного меню File/New (Файл/Новый). После чего перейдите в открывшемся окне New Items (Новые объекты) на вкладку ActiveX (рис. 3.15).
Выберите пиктограмму Automation Object (Объект автоматизации) двойным щелчком. После этого появится окно Automation Object Wizard (Мастер объекта автоматизации) (рис. 3.16).

Создание сервера автоматизации

Рис. 3.15. Вкладка ActiveX окна New Items

Создание сервера автоматизации

Рис. 3.16. Диалоговое окно Automation Object Wizard
Впишем имя Auto в поле CoClass Name для СОМ-класса объекта автоматизации. Мастер автоматически добавит букву т к имени класса, если создается класс Object Pascal для объекта автоматизации, или букву i - при создании основного интерфейса для объекта автоматизации.
Возможные -значения остальных полей ввода нами уже рассматривались в предыдущей главе.
После установки всех параметров в данном диалоговом окне Delphi создает новую библиотеку типа для проекта, и, кроме того, добавит интерфейс и класс компонентов к данной библиотеке типа. Более того, мастер создаст новый модуль в нашем проекте, который содержит реализацию интерфейса автоматизации, добавленного к библиотеке типа. На рис. 3.17 изображен редактор библиотеки типа сразу же после закрытия диалогового окна Automation Object Wizard (Мастер объекта автоматизации).

Создание сервера автоматизации

Рис. 3.17. Новый проект автоматизации в редакторе библиотеки типа
В листинге 3.4 приведен код нового модуля, который генерирует мастер объекта автоматизации Delphi.

Листинг 3.4.
unit Unit2; interface
uses
ComObj, ActiveX, Projectl_TLB, StdVcl;
type
TAuto = class(TAutoObject, lAuto)
protected
{ Protected declarations }
end;
implementation
uses ComServ;
initialization
TAutoObjectFactory.Create(ComServer, TAuto, Class_Auto,ciMultilnstance, tmApartment);
end.

Из вышеприведенного листинга ясно, что новый объект автоматизации TAuto является классом-наследником класса TAutoObject. Отметим, что класс TAutoobject является базовым классом всех объектов автоматизации.
Теперь, после добавления к нашему проекту нового объекта автоматизации TAuto мы можем дописать к основному интерфейсу lAuto необходимые свойства и методы.
Поставим перед собой простую задачу. Допустим, наш сервер автоматизации должен изменять цвет формы путем передачи необходимого цвета из диспетчера автоматизации. Для решения данной задачи добавим свойство color к интерфейсу lAuto. Это можно сделать в редакторе библиотеки типов разными способами, один из которых такой:
- щелкните левой кнопкой мыши на изображение интерфейса lAuto в левой части редактора библиотеки типа;
- т. к. нам нужно только получать цвет от диспетчера автоматизации, то нам понадобится свойство только для чтения, для этого щелкните на стрелочке справа от пиктограммы, изображающей свойство в верхней панели редактора, и выберите Write Only (Только для записи) (рис. 3.18);
- назовите свойство color и установите тип свойства OLE_COLOR в поле туре.

Создание сервера автоматизации

Рис. 3.18. Добавление свойства только для чтения в интерфейс lAuto
Все. Новое свойство добавлено, теперь нажмите в верхней панели редактора кнопку Refresh Implementation для обновления модуля реализации объекта автоматизации (рис. 3.19). В результате Delphi подкорректирует модуль реализации в соответствии с теми изменениями, которые были нами внесены в редакторе библиотеки. Код измененного модуля представлен на листинге 3.5. Кроме того, в данном листинге вручную дописано тело процедуры TAuto.Set_Color.

Листинг 3.5
unit Unit2;
interface
uses
ComObj, ActiveX, TestJTLB, StdVcl;
type
TAuto = class(TAutoObject, lAuto)
protected
procedure Set_Color(Value: OLE_COLOR); safecall;
{ Protected declarations }
end;
implementation
uses ComServ, Unitl;
procedure TAuto.Set_Color(Value: OLE_COLOR);
begin
Forml.color:=Value;
end;
initialization
TAutoObjectFactory.Create(ComServer, TAuto, Class_Auto, ciMultilnstance, tmApartment);
end.

После этого создайте на жестком диске новую папку. Назовем ее, например, Automation. Сохраним наше приложение в эту папку. При сохранении, дайте проекту имя Test. Затем откомпилируйте или запустите приложение для того, чтобы получить исполняемый файл Test.exe.
Итак, наш новый сервер автоматизации готов. Для того чтобы убедиться, что он работает, создадим диспетчер автоматизации. Для этого закроем текущий проект File/Close All (Файл/Закрыть все) и создадим новый File/New Application. Разместим на форме две кнопки: Соединение и Изменить цвет (рис. 3.20).

Создание сервера автоматизации

Рис. 3.19. Законченная библиотека типов

Создание сервера автоматизации

Рис. 3.20. Внешний вид формы диспетчера автоматизации
Приведенный листинг 3.6 иллюстрирует код для нашего диспетчера автоматизации.

Листинг 3.6
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, TestJTLB;
type
TForml = class(TForm)
Buttonl: TButton;
Button2: TButton;
procedure ButtonlClick(Sender: TObject);
procedure,Button2Click(Sender: TObject);
private
FIntf: lAuto;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation {$R *.DFM}
procedure TForml.ButtonlClick(Sender: TObject);
begin
FIntf:=CoAuto.create ;
end;
procedure TForml.Button2Click(Sender: TObject);
begin
FIntf.Set_Color(clRed);
end;
end.

Рассмотрим вышеприведенный листинг. Итак, для начала нужно добавить в раздел uses библиотеку типов Test_TLB. Далее в разделе private добавим поле FIntf типа lAuto. После чего для кнопки Соединение пишем обработчик события Onclick, которое создает экземпляр сервера автоматизации. При нажатии на эту кнопку будет запущено приложение Test.exe. Для события Onclick кнопки Изменить цвет пишем обработчик события, который вызывает метод set_color интерфейса lAuto. Установим, например, красный цвет clRed формы приложения-сервера. Запустим наш диспетчер автоматизации на исполнение. Нажмем последовательно кнопки Соединение и Изменить цвет. Вы можете видеть, как сначала запускается приложение Test.exe, а затем цвет формы приложения Test.exe изменяется на красный.
Наше простейшее приложение, использующее автоматизацию успешно работает (рис. 3.21).

Создание сервера автоматизации

Рис. 3.21. Результат работы приложения автоматизации

Глава 10 Содержание Глава 12

Управление сервером автоматизации

Рассмотрим в качестве примера применения свойства ConnectKind подключение и управление сервером автоматизации. Выполните последовательно следующие шаги:
1. При помощи пункта главного меню File/New Application (Файл/Новое приложение) создайте новый проект.
2. Поместите на форму компоненты wordApplication и wordDocument, расположенные на вкладке Servers.
3. Установите свойства AutoConnect и AutoQuit для компонента WordApplication В true.
4. Установите свойство ConnectKind для компонента WordDocument в CkAttachToInterface.
5. Выберите на форме компонент wordApplication и в окне инспектора объектов перейдите на вкладку Events (События).
6. Дважды щелкните на событии onDocumentchange и запишите в заготовке обработчика события, которую создаст Delphi, приведенный на листинге 3.2 код

Листинг З.2
procedure TForml.WordApplicationlDocumentChange(Sender: TObject);
begin
// Производим подключение к активному документу Microsoft Word
WordDocumentl.ConnectTo(WordApplicationl.ActiveDocument};
// Наш диспетчер автоматизации добавляет новую строку в текущий документ
WordDocumentl.Range.InsertAfter(#13+'Переход к документу'+#13+
WordApplicationl.ActiveDocument.Get_FullName+' произведен :'+
DateTimeToStr(Now));
end;

Рассмотрим, какие действия произведет код, представленный на листинге 3.2. Во-первых, данный код будет выполняться всякий раз при смене текущего документа Microsoft Word. После смены текущего документа (то есть, простого переключения между несколькими документами Microsoft Word) наш диспетчер автоматизации при помощи метода ConnectTo подключится к активному документу. Затем, при помощи метода InsertAfter производится вставка текстовой строки в текущий документ. Переход на новую строку в документе осуществляется при помощи вставки символа перевода строки (#13). Метод Get_FullName позволяет получить название текущего документа Microsoft Word.
7. Для события формы Create напишите код, представленный на листинге 3.3.

Листинг З.3
procedure TForml.FormCreate(Sender: TObject);
begin
// Отображение сервера автоматизации на экране
WordApplicationl.Visible:=true;
end;

Данный код отобразит Microsoft Word на экране, даже если он ранее не был загружен.
Запустите созданный вами диспетчер автоматизации при помощи пункта главного меню Run/Run (Запуск/Запуск). После старта приложения будет автоматически загружен Microsoft Word. С помощью пункта меню Microsoft Word File/New (Файл/Создать) создайте несколько новых документов. Теперь убедимся, что наш диспетчер автоматизации работает. Попробуйте переключаться между документами при помощи раздела меню Microsoft Word Window (Окно). Вы можете видеть, как диспетчер автоматизации добавляет новые строки в текущий документ.
Такой же принцип управления можно применять и в случае, когда необходимо контролировать действия Microsoft Excel. Когда в Microsoft Excel создается новая книга, возникает событие OnNewworkBook.

ActiveX в Delphi

Что такое элемент управления ActiveX?

Ответ на вопрос, заданный в заголовке данного раздела, может быть разным. Все зависит от того, с какой точки зрения смотреть на ActiveX. С точки зрения разработчика, элемент управления ActiveX - это нечто, обладающее свойствами, событиями и методами (практически как любой другой компонент). Разработчик на Delphi может не иметь представления о том, что такое СОМ, и, в то же время, успешно использовать элементы управления ActiveX в своих приложениях.
С точки зрения компонентной модели объектов (СОМ), элемент управления ActiveX представляет собой сервер автоматизации, который реализован в виде DLL и исполняемый в одном процессе с вашим приложением. Элементы управления ActiveX допускают визуальное редактирование, т. е. вы можете изменять значения их свойств, методов, писать обработчики событий точно так же, как вы делали это для компонентов из VCL Delphi. Вообще, следует заметить, что идея технологии ActiveX была частично реализована достаточно давно. Еще в Microsoft Visual Basic для разработки 16-разрядных приложений были использованы так называемые модули расширения VBX. Разработчики быстро поняли все преимущества данной технологии и создали тысячи модулей VBX. Идея компонентной разработки понравилась многим, и по ее принципу стали создавать многие средства для разработки приложений, к числу которых относится и Delphi.
Данная технология постепенно переросла в 32-разрядную технологию ActiveX.

Необходимость использования ActiveX

Вам могло и не приходить в голову, что вы уже использовали элементы управления ActiveX при написании своих приложений. Дело в том, что многие элементы управления ActiveX, которые зарегистрированы в системном реестре Windows, уже установлены в палитру компонентов Delphi и внешне ничем не отличаются от обычных компонентов Delphi. Мы не будем останавливаться на том, какие компоненты являются элементами управления. Вместо этого посмотрим, когда возникает необходимость использовать ActiveX.
Обычно такая необходимость возникает, когда вы хотите расширить функциональные возможности своего приложения за счет возможностей уже зарегистрированных в системе приложений. Например, если вы хотите создать собственный обозреватель Web, вам необязательно начинать писать его с нуля. Есть ведь великолепное ядро для обозревателя Web, которое используется в приложении Microsoft Internet Explorer (SHDOCVW.DLL). Вашей задачей здесь является лишь подключение данного ядра (которое является элементом управления ActiveX) к своему приложению и работа с ним.
Отметим, что в пятой версии Delphi в палитру компонентов была добавлена вкладка Internet, которая предоставляет возможность разработчику создавать приложения на основе ядра Microsoft Internet Explorer.

Внесение элемента управления ActiveX в палитру компонентов

Вместе с Delphi поставляется несколько компонентов ActiveX, которые были сделаны различными разработчиками, но, скорее всего, вам понадобится самим добавлять новые элементы ActiveX в палитру компонентов Delphi.
Установка нужного элемента управления ActiveX начинается с выбора пункта главного меню Component/Import ActiveX Control (Компонент/Импорт элемента управления ActiveX). Появится диалоговое окно (рис. 3.22), содержащее сведения обо всех элементах управления ActiveX, которые были зарегистрированы в системном реестре Windows.
Рассмотрим данное диалоговое окно.
В верхней части окна перечислены зарегистрированные в системе элементы ActiveX. Список Class names (Имена классов) отображает названия классов, имеющихся в данном элементе управления. На рис. 3.22 мы видим, что у выбранного элемента ActiveX имеются два встроенных класса TDHTMLEdit и TDHTMLSafe.
Выпадающий список Palette page (Вкладка палитры) служит для выбора вкладки палитры компонентов, на которую будет размещен выбранный элемент ActiveX. По умолчанию, это вкладка ActiveX.
Следующие два поля для ввода Unit dir name (Имя директории для модуля) и Search path (Путь для поиска) предназначены, соответственно, для указания директории, в которой будет размещен модуль элемента ActiveX, и путь поиска данного модуля для компилятора Delphi.

Внесение элемента управления ActiveX в палитру компонентов

Рис. 3.22. Диалоговое окно импорта элементов управления ActiveX
Выберите тот же элемент управления ActiveX, какой изображен на рис. 3.22. Если такого элемента у вас нет, то возьмите любой другой, но в дальнейшем мы будем описывать установку именно этого элемента управления.
После того как вы выбрали данный элемент управления, нажмите кнопку Install (Установить). Появится окно Install (Установка) (рис. 3.23).

Примечание
Другая кнопка, Create Unit (Создать модуль), позволяет создать так называемый файл представления (wrapper). Данный файл - это описание библиотеки типов, он содержит описание всех методов, событий и свойств, которые находятся в элементе управления. Содержимое данного файла написано на языке Object Pascal. Имя файла состоит из двух частей: имени элемента ActiveX и строки _TLB.PAS. Нажав кнопку Create Unit (Создать модуль) вы лишь создадите данный файл, после чего можете посмотреть его содержимое. Для продолжения установки элемента управления вам нужно воспользоваться кнопкой Install (Установка). Данное окно содержит две вкладки Into existing package (В существующий пакет) и Into new package (В новый пакет). Вы должны выбрать, в какой пакет хотите включить новый элемент управления.

Примечание
Рекомендуется для элементов управления ActiveX создать свой собственный пакет. Это позволит экономнее использовать ресурсы. Мы в целях упрощения описания установки не будем создавать новый пакет.

Добавим наш элемент управления в пакет, предлагаемый Delphi по умолчанию (dclusrSO.dpk).

Внесение элемента управления ActiveX в палитру компонентов

Рис. 3.23. Окно установки элемента управления в пакет
После нажатия кнопки ОК в окне Install (Установка) Delphi откомпилирует пакет dclusrSO.dpk. В результате, содержимое пакета должно выглядеть, как представлено на рис. 3.24.

Внесение элемента управления ActiveX в палитру компонентов

Рис. 3.24. Содержимое пакета dclusr5O.dpk после установки нового элемента управления ActiveX
Теперь перейдем к вкладке ActiveX палитры компонентов и посмотрим, какие изменения там произошли (рис. 3.25).

Внесение элемента управления ActiveX в палитру компонентов

Рис. 3.25. Содержимое вкладки ActiveX
Как вы можете видеть, на вкладке появились пиктограммы двух новых компонентов: DHTMLEdit И DHTMLSafе.
Теперь вы можете размещать любой из этих компонентов на ваших формах и пользоваться ими так же, как и любыми другими компонентами Delphi, То есть, используя инспектор объектов, вы можете установить значения необходимых свойств, написать обработчики событий, и т. д.
Для того чтобы деинсталлировать элемент управления ActiveX, вам нужно открыть файл пакета, в который был установлен ActiveX, и убрать ненужные элементы. После этого необходимо заново откомпилировать пакет. Эти действия приведут к удалению элементов с палитры компонентов.

Глава 11 Содержание Глава 13

ActiveX в Delphi

Разработка элементов управления ActiveX

Следует отметить, что как таковой мастер для создания элементов управления ActiveX в Delphi отсутствует. Разработчик обычно выбирает один из двух возможных путей:
- использование мастера превращения любого оконного компонента (потомка TWinControl) в элемент управления ActiveX. Для этого применяется мастер, который вы можете вызвать с помощью пункта главного меню File/New (Файл/Новый), и перейдя на вкладку ActiveX выбрать пиктограмму ActiveX Control;
- использование мастера преобразования форм в элемент ActiveX. Для этого вы можете вызвать мастер, расположенный на вкладке ActiveX (как в предыдущем варианте), при этом выбрав пиктограмму ActiveForm.

Создание ActiveX из форм

Данный способ создания элементов управления позволяет на базе собственной формы приложения создать элемент ActiveX. Этот способ носит название ActiveForms.
Попробуем создать элемент управления ActiveX из формы. Выполним следующие шаги:
1. Вызовем мастер ActiveForm. Для этого выберем пункт главного меню File/New (Файл/Новый), и перейдя на вкладку ActiveX выберем пиктограмму ActiveForm. Появится окно мастера (рис. 3.28).
Как можно видеть, данный мастер очень похож на мастера преобразования компонента VCL Delphi в элемент управления ActiveX. Но отличие сразу бросается в глаза. В поле имени класса VCL Class Name (Имя класса VCL) мастер автоматически устанавливает значение TActiveForm.

Создание ActiveX из форм

Рис. 3.28. Диалоговое окно мастера ActiveForm
2. В поле New ActiveX Name (Новое имя ActiveX) нам нужно ввести имя создаваемого элемента управления ActiveX. Введем имя MyForm, при этом автоматически произойдут изменения в других полях (они описаны выше для мастера ActiveX Control).
3. После нажатия кнопки ОК мастер создает все необходимые заготовки для элемента управления ActiveX (практически так же, как и в предыдущем случае). Поверхность формы MyForm становится рабочей поверхностью. Вы можете размещать на ней произвольные компоненты, описывать методы и события компонентов и формы, устанавливать их свойства.
Разместим на форме MyForm два компонента CоmВох, как показано на рис. 3.29.

Создание ActiveX из форм

Рис. 3.29. Форма MyForm
Внесем в свойство items для первого компонента СотЬоВох значения, соответствующие названиям месяцев с января по декабрь. В свойство items второго компонента СотЬоВох внесем значения, которые соответствуют названиям дней недели с понедельника по воскресенье. Итак, при нажатии на первый список в нем должны отображаться названия месяцев, а при нажатии кнопки второго списка - названия дней недели.
В принципе, мы уже создали элемент управления ActiveX. Но данный элемент управления будет бесполезным, т. к. нам необходимо, чтобы выбранные в списках значения передавались пользователю. Для этого нужно создать интерфейс с пользователем элемента управления.
Для решения данной задачи нам необходимо добавить в библиотеку типов элемента управления в интерфейс IMyForm два новых свойства. Назовем их, например, Month и Day. Откроем редактор библиотеки типов при помощи пункта главного меню View/Type Library (Просмотр/Библиотека типов). Работать с ней нам уже приходилось, поэтому не будет сложным добавить к интерфейсу IMyForm свойства Month и Day (рис. 3.30).
Теперь нужно обновить модуль реализации MyFormImpl1. Для чего необходимо нажать кнопку Refresh Implementation (Обновить реализацию) в верхней панели инструментов редактора библиотеки типов. При этом в обоих файлах проекта появляются описания новых свойств, а в файле реализации - заготовки кода (листинг 3.10).

Листинг З.10
function TMyForm.Get_Day: Integer;
begin
end;
function TMyForm.Get_Month: Integer;
begin
end;
procedure TMyForm.Set_Day(Value: Integer);
begin
end;
procedure TMyForm.Set_Month(Value: Integer);
begin
end;

Создание ActiveX из форм

Рис. 3.30. Редактор библиотеки типов с добавленными в интерфейс IMyForm свойствами Month и Day
Как мы видим, были добавлены две заготовки для функций Get и две заготовки для процедур set. Функции Get предназначены для передачи значения свойства в программу, вызвавшую данную функцию. Процедуры Set служат для передачи и установки необходимых параметров.
Заполним эти заготовки следующим образом (листинг 3.11):

Листинг 3.11
function TMyForm.Get_Day: Integer;
begin
Result:=ComboBox2.Itemlndex;
end;
function TMyForm.Get_Month: Integer;
begin
Result: =ComboBoxl. Itemlndex;
end;
procedure TMyForm.Set_Day(Value: Integer);
begin
ComboBox2.Itemlndex:=value;
end;
procedure TMyForm.Set_Month(Value: Integer);
begin
ComboBoxl.Itemlndex:=value;
end;

Передаваемые и принимаемые значения свойств Day и Month в нашем случае имеют тип integer, хотя вы могли присвоить данным свойствам другой тип в редакторе библиотеки типов в процессе создания этих свойств.
Вот и все. Мы создали элемент управления ActiveX на основе формы. Теперь осталось лишь откомпилировать проект и установить элемент управления в палитру компонентов (как это сделать, описано в конце предыдущего раздела).
Попробуем использовать данный элемент управления ActiveX в своем приложении. Создадим новый проект. Разместим на нем элемент управления ActiveX MyForm. Кроме того, расположим на форме четыре кнопки и два компонента Edit (рис. 3.31).

Создание ActiveX из форм

Рис. 3.31. Внешний вид приложения, использующего элемент управления ActiveX MyForm
На листинге 3.12 приведен код для приложения, внешний вид которого представлен на рис. 3.31.

Листинг 3.12
unit Unitl;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, OleCtrls, MyFormProjlJTLB;
type
TForml = class(TForm)
MyForml: TMyForm;
GetMonth: TButton;
Editl: TEdit;
GetDay: TButton;
Edit2: TEdit;
SetMonth: TButton;
SetDay: TButton;
Label1: TLabel;
Label2: TLabel;
procedure GetMonthClick(Sender: TObject);
procedure GetDayClick(Sender: TObject);
procedure SetMonthClick(Sender: TObject)';
procedure SetDayClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Forml: TForml;
implementation {$R *.DFM}
procedure TForml.GetMonthClick(Sender: TObject);
begin
Editl.Text:=IntToStr(MyForml.Month+1);
end;
procedure TForml.GetDayClick(Sender: TObject);
begin
Edit2.Text:=IntToStr(MyForml.Day+1);
end;
procedure TForml.SetMonthClick(Sender: TObject);
begin
MyForml.Month:=StrToInt(Edit1.Text)-1;
end;
procedure TForml.SetDayClick(Sender: TObject);
begin
MyForml.Day:=StrToInt(Edit2.Text)-1;
end;
end.

При нажатии на любую из двух кнопок, размещенных на форме слева (GetMonth и GetDay), вы получите порядковый номер значения из нужного списка компонента MyForm (т. к. нумерация элементов списка CоmВох на чинается с нуля, то мы прибавляем к получаемому значению единицу) в соответствующее поле редактирования Edit. Нажатие любой из кнопок справа на форме (setMonth и setDay) приведет к передаче значения из необходимого поля редактирования Edit в элемент управления ActiveX. При этом произойдет установка соответствующего свойства элемента MyForm.
Работу данного приложения иллюстрирует рис. 3.32.

Создание ActiveX из форм

Рис. 3.32. Приложение, использующее ActiveX в процессе исполнения

Глава 12 Содержание Глава 14

Создание ActiveX из компонентов VCL Delphi

Итак, одной из возможностей создания элемента управления ActiveX в Delphi - это превращение любого оконного компонента VCL Delphi в ActiveX.

Примечание
Несмотря на то, что мастер ActiveX Control не дает возможности автоматически создавать элементы управления ActiveX из компонентов, не являющихся потомками TWinControl, разработчик может создать такой элемент управления самостоятельно при помощи системы разработки Delphi ActiveX (DAX).

Вызовем мастера ActiveX Control. Появится окно мастера создания элемента ActiveX из оконного компонента Delphi (рис. 3.26).
В процессе использования данного мастера вам нужно выполнить следующее:
1. Выбрать компонент VCL из выпадающего списка VCL Class Name (Имя класса VCL).

Примечание
Как вы, вероятно, заметили, в выпадающем списке находятся далеко не все компоненты VCL. В данный список внесены только те компоненты, которые удовлетворяют следующим требованиям: находятся в текущем пакете, который используется в процессе разработки (следовательно, находятся в палитре компонентов); являются потомками компонента TWinControl; не исключаются из данного списка при помощи процедуры RegisterNonActiveX.

Многие из компонентов исключены из списка из-за того, что они либо не могут быть элементами управления ActiveX (например, компонент TDBGrid, т. к. для его работы нужен другой VCL-класс TDataSource), либо необходимо самостоятельно скорректировать их перед запуском мастера, чтобы они могли функционировать как элементы ActiveX (например, TTreeView, т. к. узлы компонента очень сложно представлять в ActiveX).
2. Выбрать новое имя ActiveX в поле для ввода New ActiveX Name (Новое имя ActiveX), если вас не устраивает имя, предлагаемое Delphi автоматически.
3. Выбрать имя модуля реализации создаваемого элемента ActiveX в поле для ввода Implementation Unit (Модуль реализации).
4. Выбрать имя проекта в поле для ввода Project Name (Имя проекта).
5. Выбрать потоковую модель в выпадающем списке Threading Model (Модель потока).
6. После этого вы можете установить три дополнительные опции:
Include About Box (Включить окно "О программе") - включить в проект диалоговое окно, которое будет содержать информацию о разработчике. Данное диалоговое окно представляет собой обычную форму Delphi;
Include Version Information (Включить информацию о версии) - включить в проект информацию о версии элемента управления ActiveX (данный пункт требуется для некоторых контейнеров, например, созданных в Visual Basic 4.0);
Make Control Licensed (Включить лицензионную информацию) - включить в проект лицензионную информацию (в случае коммерческого распространения элемента управления). В результате использовать данный элемент управления смогут только те разработчики, которые имеют лицензионный ключ, причем данное ограничение будет работать во всех средах разработки (Delphi, Visual Basic и др.). При установке данного флажка вместе с проектом элемента управления должен быть сгенерирован файл лицензии, имеющий расширение LIC. Чтобы дать возможность другим разработчикам использовать этот ActiveX, вам придется передавать лицензионным пользователям файл лицензии вместе с элементом управления ActiveX (имеющим расширение OCX).

Создание ActiveX из компонентов VCL Delphi

Рис. 3.26. Мастер преобразования компонентов VCL Delphi в ActiveX
После внесения необходимых значений в поля, мастер автоматически создаст все файлы, которые нужны для поддержания данного элемента управления. В число таких файлов входят: сам проект, библиотека типов и файл реализации. Обратите внимание на то, что все элементы ActiveX являются либо библиотеками DLL, либо файлами OCX (что, в принципе, одно и то же).
Рассмотрим действия, которые выполняет мастер в процессе создания элемента управления ActiveX из компонента:
- мастер определяет, в каком модуле содержится элемент управления VCL. Данный модуль передается компилятору, который создает специальную символьную информацию для описания свойств, событий и методов элемента управления VCL;
- генерируется библиотека типов для проекта;
- мастер анализирует всю символьную информацию об элементе управления VCL и добавляет подходящие свойства и методы к интерфейсу и в библиотеку типов;

Примечание
Для определения, подходит ли данное свойство, метод или событие для включения в библиотеку типов, мастер проверяет, совместим ли с автоматизацией тип свойства, его параметры, а также возвращаемые значения событий и методов. С автоматизацией совместимы следующие типы: Byte, Smallint, Integer, Single, Double, Currency, TDateTime, WideString, WordBool, PSafeArray, TDecimal, OleVariant, lUnknown, IDispatch. Кроме перечисленных, разрешены параметры типов TStrings, TPicture и TFont. Для данных типов мастер создаст промежуточные объекты, которые позволят им быть инкапсулированными в IDispatch или Dispinterface.

- редактор библиотеки типов генерирует файл преобразованной библиотеки типов в соответствии с добавленными свойствами, событиями и методами;
- мастер создает файл реализации элемента управления ActiveX. Данный файл содержит элемент управления TActivexcontroi. Кроме того, мастер автоматически создает так называемые пересылки (forwarders) свойств и методов интерфейса. Пересылки направляют вызовы метода из элемента управления ActiveX в элемент управления VCL, а события - из элемента управления VCL в элемент ActiveX.
Листинг 3.7 содержит код файла проекта элемента управления ActiveX, созданного на основе компонента TRichEdit.

Листинг 3.7
library RichEditXControll;
uses
ComServ,
RichEditXControllJTLB in 'RichEditXControllJTLB.pas',
RichEditlmpll in 'RichEditlmpll.pas' {RichEditX: CoClass};
{$E ocx}
exports
DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer;
{$R *.TLB}
{$R *.RES}
begin
end.

Описывать данный листинг не имеет особого смысла, т. к. его содержание достаточно очевидно.
Листинг 3.8 содержит код реализации элемента ActiveX.

Листинг 3.8
unit RichEditlmpll;
interface
uses
Windows, ActiveX, Classes, Controls, Graphics, Menus, Forms, StdCtrls, ComServ, StdVCL, AXCtrls, RichEditXControllJTLB, ComCtrls;
type
TRichEditX = class(TActiveXControl, IRichEditX)
private
{ Private declarations }
FDelphiControl: TRichEdit;
FEvents: IRicnEditXEvents;
procedure ChangeEvent(Sender: TObject);
procedure KeyPressEvent(Sender: TObject; var Key: Char);
procedure ProtectChangeEvent(Sender: TObject; StartPos, EndPos:
Integer;
var AllowChange: Boolean);
procedure SaveClipboardEvent(Sender: TObject; NumObjects, NumChars: Integer; var SaveClipboard: Boolean);
procedure SelectionChangeEvent(Sender: TObject);
protected
{ Protected declarations }
procedure DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage); override;
procedure EventSinkChanged(const EventSink: lUnknown); override;
procedure InitializeControl; override;
function Get_Alignment: TxAlignment; safecall;
function Get_BorderStyle: TxBorderStyle; safecall;
function Get_CanUndo: WordBool; safecall;
function Get_Color: OLE_COLOR; safecall;
function Get_Ctl3D: WordBool; safecall;
function Get_Cursor: Smallint; safecall;
function Get_DoubleBuffered: WordBool; safecall;
function Get_DragCursor: Smallint; safecall;
function Get_DragMode: TxDragMode; safecall;
function Get_Enabled: WordBool; safecall;
function Get_Font: IFontDisp; safecall;
function Get_HideScrollBars: WordBool; safecall;
function Get_HideSelection: WordBool; safecall;
function Get_ImeMode: TxImeMode; safecall;
function Get_ImeName: WideString; safecall;
function Get_Lines: IStrings; safecall;
function Get_MaxLength: Integer; safecall;
function Get_Modified: WordBool; safecall;
function Get_ParentColor: WordBool; safecall;
function Get_ParentCtl3D: WordBool; safecall;
function Get_PlainText: WordBool; safecall;
function Get_ReadOnly: WordBool; safecall;
function Get_ScrollBars: TxScrollStyle; safecall;
function Get_SelLength: Integer; safecall;
function Get_SelStart: Integer; safecall;
function Get_SelText: WideString; safecall;
function Get_Text: WideString; safecall;
function Get_Visible: WordBool; safecall;
function Get_VisibleDockClientCount: Integer; safecall;
function Get_WantReturns: WordBool; safecall;
function Get_WantTabs: WordBool; safecall;
function Get_WordWrap: WordBool; safecall;
procedure _Set_Font(const Value: IFontDisp); safecall;
procedure Set_Alignment(Value: TxAlignment); safecall;
procedure Set_BorderStyle(Value: TxBorderStyle); safecall;
procedure Set_Color(Value: OLE_COLOR); safecall;
procedure Set_Ctl3D(Value: WordBool); safecall;
procedure Set_Cursor(Value: Smallint); safecall;
procedure Set_DoubleBuffered(Value: WordBool); safecall;
procedure Set_DragCursor(Value: Smallint); safecall;
procedure Set_DragMode(Value: TxDragMode); safecall;
procedure Set_Enabled(Value: WordBool); safecall;
procedure Set_Font(var Value: IFontDisp); safecall;
procedure Set_HideScrollBars(Value: WordBool); safecall;
procedure Set_HideSelection(Value: WordBool); safecall;
procedure Set_ImeMode(Value: TxImeMode); safecall;
procedure Set_ImeName(const Value: WideString); safecall;
procedure Set_Lines(const Value: IStrings); safecall;
procedure Set_MaxLength(Value: Integer); safecall;
procedure Set_Modified (Value: WordBool); safecall;
procedure Set_ParentColor(Value: WordBool); safecall;
procedure Set_ParentCtl3D(Value: WordBool); safecall;
procedure Set_PlainText(Value: WordBool); safecall;
procedure Set_ReadOnly(Value: WordBool); safecall;
procedure Set_ScrollBars(Value: TxScrollStyle); safecall;
procedure Set_SelLength(Value: Integer); safecall;
procedure Set_SelStart(Value: Integer); safecall;
procedure Set_SelText(const Value: WideString); safecall;
procedure Set_Text(const Value: WideString); safecall;
procedure Set_Visible(Value: WordBool); safecall;
procedure Set_WantReturns(Value: WordBool); safecall;
procedure Set_WantTabs(Value: WordBool); safecall;
procedure Set_WordWrap(Value: WordBool); safecall;
end;
implementation
uses ComObj;
{ TRichEditX }
procedure TRichEditX.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage);
begin
{TODO: Определите здесь страницы свойств. Они определяются путем вызова метода DefinePropertyPage с идентификатором класса.
Например, DefinePropertyPage(Class_RichEditXPage); }
end;
procedure TRichEditX.EventSinkChanged(const EventSink: lUnknown);
begin
FEvents := EventSink as IRichEditXEvents;
end;
procedure TRichEditX.InitialiZeControl;
begin
FDelphiControl :=* Control as TRichEdit;
FDelphiControl.OnChange := ChangeEvent;
FDelphiControl.OnKeyPress := KeyPressEvent;
FDelphiControl.OnProtectChange := ProtectChangeEvent;
FDelphiControl.OnSaveClipboard := SaveClipboardEvent;
FDelphiControl.OnSelectionChange := SelectionChangeEvent;
end;
function TRichEditX.Get_Alignment: TxAlignment;
begin
Result := Ord(FDelphiControl.Alignment);
end;
function TRichEditX.Get_BorderStyle: TxBorderStyle;
begin
Result := Ord(FDelphiControl.BorderStyle);
end;
function TRichEditX.Get_CanUndo: WordBool;
begin
Result := FDelphiControl.CanUndo;
end;
function TRichEditX.Get_Color: OLE_COLOR;
begin
Result := OLE_COLOR(FDelphiControl.Color);
end;
function TRichEditX.Get_Ctl3D: WordBool;
begin
Result := FDelphiControl.Ctl3D;
end;
function TRichEditX.Get_Cursor: Smallint;
begin
Result := Smallint(FDelphiControl.Cursor);
end;
function TRichEditX.Get_DoubleBuffered: WordBool;
begin
Result := FDelphiControl.DoubleBuffered;
end;
function TRichEditX.Get_DragCursor: Smallint;
begin
Result := Smallint(FDelphiControl.DragCursor);
end;
function TRichEditX.Get_DragMode: TxDragMode;
begin
Result := Ord(FDelphiControl.DragMode);
end;
function TRichEditX.Get_Enabled: WordBool;
begin
Result := FDelphiControl.Enabled;
end;
function TRichEditX.Get_Font: IFontDisp;
begin
GetOleFont(FDelphiControl.Font, Result) ;
end;
function TRichEditX.Get_HideScrollBars: WordBool;
begin
Result := FDelphiControl.HideScrollBars;
end;
function TRichEditX.Get_HideSelection: WordBool;
begin
Result := FDelphiControl.HideSelection;
end;
function TRichEditX.Get_ImeMode: TxImeMode;
begin
Result := Ord(FDelphiControl.ImeMode);
end;
function TRichEditX.Get_ImeName: WideString;
begin
Result := WideString(FDelphiControl.ImeName);
end;
function TRichEditX.Get_Lines: IStrings;
begin
GetOleStrings(FDelphiControl.Lines, Result);
end;
function TRichEditX.Get_MaxLength: Integer;
begin
Result := FDelphiControl.MaxLength;
end;
function TRichEditX.Get_Modified: WordBool;
begin
Result := FDelphiControl.Modified;
end;
function TRichEditX.Get_ParentColor: WordBool;
begin
Result := FDelphiControl.ParentColor;
end;
function TRichEditX.Get_ParentCtl3D: WordBool;
begin
Result := FDelphiControl.ParentCtlSD;
end;
function TRichEditX.Get_PlainText: WordBool;
begin
Result := FDelphiControl.Plaintext;
end;
function TRichEditX.Get_ReadOnly: WordBool;
begin
Result := FDelphiControl.Readonly;
end;
function TRichEditX.Get_ScrollBars: TxScrollStyle;
begin
Result := Ord(FDelphiControl.ScrollBars);
end;
function TRichEditX.Get_SelLength: Integer;
begin
Result := FDelphiControl.SelLength;
end;
function TRichEditX,Get_SelStart: Integer;
begin
Result := FDelphiControl.SelStart;
end;
function TRichEditX.Get_SelText: WideString;
begin
Result := WideString(FDelphiControl.SelText);
end;
function TRichEditX.Get_Text: WideString;
begin
Result := WideString(FDelphiControl.Text);
end;
function TRichEditX.Get_Visible: WordBool;
begin
Result := FDelphiControl.Visible;
end;
function TRichEditX.Get_VisibleDockClientCount: Integer;
begin
Result := FDelphiControl.VisibleDockClientCount;
end;
function TRichEditX.Get_WantReturns: WordBool;
begin
Result := FDelphiControl.WantReturns;
end;
function TRichEditX.Get_WantTabs: WordBool;
begin
Result := FDelphiControl.WantTabs;
end;
function TRichEditX.Get_WordWrap: WordBool;
begin
Result := FDelphiControl.Wordwrap;
end;
procedure TRichEditX._Set_Font(const Value: IFontDisp);
begin
SetOleFont(FDelphiControl.Font, Value);
end;
procedure TRichEditX.ChangeEvent(Sender: TObject);
begin
if FEvents <> nil then FEvents.OnChange;
end;
procedure TRichEditX.KeyPressEvent(Sender: TObject; var Key: Char);
var
TempKey: Smallint;
begin
TempKey := Smallint(Key);
if FEvents <> nil then FEvents.OnKeyPress(TempKey);
Key := Char(TempKey);
end;
procedure TRichEditX.ProtectChangeEvent(Sender: TObject; StartPos, EndPos: Integer; var AllowChange: Boolean);
var
TempAllowChange: WordBool;
begin
TempAllowChange := WordBool(AllowChange);
if FEvents <> nil then FEvents.OnProtectChange(StartPos, EndPos, TempAllowChange);
AllowChange := Boolean(TempAllowChange);
end;
procedure TRichEditX.SaveClipboardEvent(Sender: TObject; NumObjects, NumChars: Integer; var SaveClipboard: Boolean);
var
TempSaveClipboard: WordBool;
begin
TempSaveClipboard := WordBool(SaveClipboard);
if FEvents <> nil then FEvents.OnSaveClipboard(NumObjects, NumChars, TempSaveClipboard); SaveClipboard := Boolean(TempSaveClipboard);
end;
procedure TRichEditX.SelectionChangeEvent(Sender: TObject);
begin
if FEvents <> nil then FEvents.OnSelectionChange;
end;
procedure TRichEditX.Set_Alignment(Value: TxAlignment);
begin
FDelphiControl.Alignment := TAlignment(Value) ;
end;
procedure TRichEditX.Set_BorderStyle(Value: TxBorderStyle);
begin
FDelphiControl.BorderStyle := TBorderStyle(Value);
end;
procedure TRichEditX.Set_Color(Value: OLE_COLOR);
begin
FDelphiControl.Color := TColor(Value);
end;
procedure TRichEditX.Set_Ctl3D(Value: WordBool);
begin
FDelphiControl.Ctl3D := Value;
end;
procedure TRichEditX.Set_Cursor(Value: Smallint);
begin
FDelphiControl.Cursor := TCursor(Value);
end;
procedure TRichEditX.Set_DoubleBuffered(Value: WordBool);
begin
FDelphiControl.DoubleBuffered := Value;
end;
procedure TRichEditX.Set_DragCursor(Value: Smallint);
begin
FDelphiControl.DragCursor := TCursor(Value);
end;
procedure TRichEditX.Set_DragMode(Value: TxDragMode);
begin
FDelphiControl.DragMode := TDragMode(Value);
end;
procedure TRichEditX.Set_Enabled(Value: WordBool);
begin
FDelphiControl.Enabled := Value;
end;
procedure TRichEditX.Set_Font(var Value: IFontDisp);
begin
SetOleFont(FDelphiControl.Font, Value);
end;
procedure TRichEditX.SetJHideScrollBars(Value: WordBool);
begin
FDelphiControl.HideScrollBars := Value;
end;
procedure TRichEditX.Set_HideSelection(Value: WordBool);
begin
FDelphiControl.HideSelection := Value;
end;
procedure TRichEditX.Set_ImeMode(Value: TxImeMode);
begin
FDelphiControl.ImeMode := TImeMode(Value);
end;
procedure TRichEditX.Set_ImeName(const Value: WideString);
begin
FDelphiControl.ImeName := TImeName(Value);
end;
procedure TRichEditX.Set_Lines(const Value: IStrings);
begin
SetOleStrings(FDelphiControl.Lines, Value);
end;
procedure TRichEditX.Set_MaxLength(Value: Integer);
begin
FDelphiControl.MaxLength := Value;
end;
procedure TRichEditX.Set_Modified(Value: WordBool);
begin
FDelphiControl.Modified := Value;
end;
procedure TRichEditX.Set_ParentColor(Value: WordBool);
begin
FDelphiControl.ParentColor := Value;
end;
procedure TRichEditX.Set_ParentCtl3D(Value: WordBool);
begin
FDelphiControl.ParentCtl3D := Value;
end;
procedure TRichEditX.Set_PlainText(Value: WordBool);
begin
FDelphiControl.PlainText := Value;
end;
procedure TRichEditX.Set_ReadOnly(Value: WordBool);
begin
FDelphiControl.Readonly := Value;
end;
procedure TRichEditX.Set_ScrollBars(Value: TxScrollStyle);
begin
FDelphiControl.ScrollBars := TScrollStyle(Value);
end;
procedure TRichEditX.Set_SelLength(Value: Integer);
begin
FDelphiControl.SelLength := Value;
end;
procedure TRichEditX.Set_SelStart(Value: Integer);
begin
FDelphiControl.SelStart := Value;
end;
procedure TRichEditX.Set_SelText(const Value: WideString);
begin
FDelphiControl.SelText := String(Value);
end;
procedure TRichEditX.Set_Text(const Value: WideString};
begin
FDelphiControl.Text := TCaption(Value);
end;
procedure TRichEditX.Set_Visible(Value: WordBool);
begin
FDelphiControl.Visible := Value;
end;
procedure TRichEditX.Set_WantReturns(Value: WordBool);
begin
FDelphiControl.WantReturns := Value;
end;
procedure TRichEditX.Set_WantTabs(Value: WordBool);
begin
FDelphiControl.WantTabs := Value;
end;
procedure TRichEditX.Set_WordWrap(Value: WordBool);
begin
FDelphiControl.Wordwrap := Value;
end;
initialization
TActiveXControlFactory.Create (ComServer, TRichEditX,TRichEdit,Class_RichEditX,
1,
''
0,
tmApartment);
end.

Описанный в данном файле класс TRichEditx является потомком TActivexcontrol. Этот класс применяется для установки соответствия между бывшим компонентом Delphi TRichEdit и контейнерами, в которых будет размещаться созданный элемент управления.
На рис. 3.27 показано окно редактора библиотеки типов для данного элемента управления ActiveX. Листинг 3.9 содержит код файла библиотеки типов.

Создание ActiveX из компонентов VCL Delphi

Рис. 3.27. Элемент управления RichEdit в редакторе библиотеки типов

Листинг 3.9
unit RichEditXControllJTLB;
// Внимание
// Типы, объявленные в этом файле, были сгенерированы из данных
// библиотеки типов. Если библиотека типов явно или неявно (ссылается
// на эту библиотеку через другую) реимпортирована или с помощью команды
// 'Refresh' в окне Type Library Editor активизирована во время
// редактирования библиотеки типов, содержимое данного файла должно быть
// регенерировано, а все внесенные вручную изменения могут быть утеряны.
*****************************************************
// PASTLWTR : $Revision: 1.88 $
// Файл создан 15.02.2001 18:30:46 из библиотеки типов, описанной ниже.
*****************************************************
// Type Lib: C:\Program
Files\Borland\Delphi5\Projects\!!!\RichEditXControll.tlb (1)
// IIDXLCID: {B1F514E7-E8BO-49B5-A6C3-F7F4910CFF9D}\0
// Helpfile:
// DepndLst:
// (1) v2.0 stdole, (C:\WINDOWS\SYSTEM\stdole2.tlb)
// (2) v4.0 StdVCL, (C:\WINDOWS\SYSTEM\STDVCL40.DLL)
*****************************************************
{$TYPEDADDRESS OFF} // Модуль должен быть откомпилирован без проверки
// типов указателей,
interface
uses Windows, ActiveX, Classes, Graphics, OleServer, OleCtrls, StdVCL;
//***********************************************
// GUIDS объявлены в TypeLibrary. Используются следующие префиксы:
// Type Libraries : LIBID_xxxx
// CoClasses : CLASS_xxxx
// DISPInterfaces : DIID_xxxx
// Non-DISP interfaces: IID_xxxx
//***********************************************
const
RichEditXControllMajorVersion = 1; RichEditXControllMinorVersion =0; '
LIBID_RichEditXControll: TGUID = '{B1F514E7-E8BO-49B5-A6C3-F7F4910CFF9D}';
IID_IRichEditX: TGUID = '{86F5C46C-CDA6-4279-8BF4-F69F1A051ED7}';
DIID_IRichEditXEvents: TGUID = '{21AB5F94-7D87-4FCO-AB5A-9BCE1D05AF8B} ' ;
CLASS_RichEditX: TGUID = '{7C731EEO-D98D-4040-8FE1-EOCB9E54C58B}';
//***********************************************
// Объявление перечислений, определенных в библиотеке типов
//***********************************************
// Константы для TxAlignment
type
TxAlignment = TOleEnum;
const
taLeftJustify = $00000000;
taRightJustify = $00000001;
taCenter = $00000002;
// Константы для TxBorderStyle
type
TxBorderStyle = TOleEnum;
const
bsNone = $00000000;
bsSingle = $00000001;
// Константы для TxDragMode
type
TxDragMode = TOleEnum;
const
dmManual = $00000000;
dmAutomatic = $00000001;
// Константы для TxImeMode
type
TxImeMode = TOleEnum;
const
imDisable = $00000000;
imClose = $00000001;
imOpen = $00000002;
imDontCare = $00000003;
imSAlpha = $00000004;
imAlpha = $00000005;
imHira = $00000006;
imSKata = $00000007;
imKata = $00000008;
imChinese = $00000009;
imSHanguel = $OOOOOOOA;
imHanguel = $OOOOOOOB;
// Константы для TxScrollStyle
type
TxScrollStyle = TOleEnum;
const
ssNone = $00000000;
ssHorizontal = $00000001;
ssVertical = $00000002;
ssBoth = $00000003;
// Константы для TxMouseButton
type
TxMouseButton = TOleEnum;
const
mbLeft = $00000000;
mbRight = $00000001;
mbMiddle = $00000002;
type
// Предварительное объявление типов, определенных в библиотеке типов
//***********************************************
IRichEditX = interface;
IRichEditXDisp = dispinterface;
IRichEditXEvents = dispinterface;
//***********************************************
// Объявление CoClasses, определенных в библиотеке типов
//***********************************************
RichEditX = IRichEditX;
//***********************************************
// Объявление структур, союзов и альянсов.
//***********************************************
PPUserTypel = AIFontDisp; {*}
//***********************************************
// Interface: IRichEditX
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {86F5C46C-CDA6-4279-8BF4-F69F1A051ED7}
//***********************************************
IRichEditX = interface(IDispatch)
['{86F5C46C-CDA6-4279-8BF4-F69F1A051ED7}']
function Get_Alignment: TxAlignment; safecall;
procedure Set_Alignment(Value: TxAlignment); safecall;
function Get_BorderStyle: TxBorderStyle; safecall;
procedure Set_BorderStyle(Value: TxBorderStyle); safecall;
function Get_Color: OLE_COLOR; safecall;
procedure Set_Color(Value: OLE_COLOR); safecall;
function Get_Ctl3D: WordBool; safecall;
procedure Set_Ctl3D(Value: WordBool); safecall;
function Get_DragCursor: Smallint; safecall;
procedure Set__DragCursor'(Value: Smallint); safecall;
function Get_DragMode: TxDragMode; safecall;
procedure Set_DragMode(Value: TxDragMode); safecall;
function Get_Enabled: WordBool; safecall;
procedure Set_Enabled(Value: WordBool); safecall;
function Get_Font: .IFontDisp; safecall;
procedure _Set_Font(const Value: IFontDisp); safecall;
procedure Set_Font(var Value: IFontDisp); safecall;
function Get_HideSelection: WordBool; safecall;
procedure Set_HideSelection(Value: WordBool); safecall;
function Get_HideScrollBars: WordBool; safecall;
procedure Set_HideScrollBars(Value: WordBool); safecall;
function Get_ImeMode: TxImeMode; safecall;
procedure Set_ImeMode(Value: TxImeMode); safecall;
function Get_ImeName: WideString; safecall;
procedure Set_ImeName(const Value: WideString}; safecall;
function Get_Lines: IStririgs; safecall;
procedure Set_Lines(const Value: IStrings); safecall;
function Get_MaxLength: Integer; safecall;
procedure Set_MaxLength(Value: Integer); safecall;
function Get_ParentColor: WordBool; safecall;
procedure Set_ParentColor(Value: WordBool); safecall;
function Get_ParentCtl3D: WordBool; safecall;
procedure Set_ParentCtl3D(Value: WordBool); safecall;
function Get_PlainText: WordBool; safecall;
procedure Set_PlainText(Value: WordBool); safecall;
function Get_ReadOnly: WordBool; safecall;
procedure Set_ReadOnly(Value: WordBool); safecall;
function Get_ScrollBars: TxScrollStyle; safecall;
procedure Set_ScrollBars(Value: TxScrollStyle); safecall;
function Get_Visible: WordBool; safecall;
procedure Set_Visible(Value: WordBool); safecall;
function Get_WantTabs: WordBool; safecall;
procedure Set_WantTabs(Value: WordBool); safecall;
function Get_WantReturns: WordBool; safecall;
procedure Set_WantReturns(Value: WordBool); safecall;
function Get_WordWrap: WordBool; safecall;
procedure Set_WordWrap(Value: WordBool); safecall;
function Get_CanUndo: WordBool; safecall;
function Get_Modified: WordBool; safecall;
procedure Set_Modifled(Value: WordBool); safecall;
function Get_SelLength: Integer; safecall;
procedure Set_SelLength(Value: Integer); safecall;
function Get_SelStart: Integer; safecall;
procedure Set_SelStart(Value: Integer); safecall;
function Get_SelText: WideString; safecall;
procedure Set_SelText(const Value: WideString); safecall;
function GetJText: WideString; safecall;
procedure Set_Text(const Value:.WideString); safecall;
function Get_DoubleBuffered: WordBool; safecall;
procedure Set_DoubleBuffered(Value: WordBool); safecall;
function Get_VisibleDockClientCount: Integer; safecall;
function Get_Cursor: Smallint; safecall;
procedure Set_Cursor(Value: Smallint); safecall;
property Alignment: TxAlignment read Get_Alignment write Set_Alignment ;
property BorderStyle: TxBorderStyle read Get_BorderStyle write Set_BorderStyle;
property Color: OLE_COLOR read Get_Color write Set_Color,;
property CtlSD: WordBool read Get_Ctl3D write Set_Ctl3D;
property DragCursor: Smallint read Get_DragCursor write Set_DragCursor;
property DragMode: TxDragMode read Get_DragMode write Set_DragMode;
property Enabled: WordBool read Get_Enabled write Set_Enabled;
property Font: IFontDisp read Get_Font write _Set_Font;
property HideSelection: WordBool read Get_HideSelection write Set_HideSelection;
property HideScrollBars: WordBool read Get_HideScrollBars write Set_HideScrollBars;
property ImeMode: TxImeMode read Get_ImeMode write Set_ImeMode;
property ImeName: WideString read Get_ImeName write Set_ImeName;
property Lines: IStrings read Get_Lines write Set_Lines;
property MaxLength: Integer read Get_MaxLength write Set_MaxLength;
property ParentColor: WordBool read Get_ParentColor write Set_ParentColor;
property ParentCtlSD: WordBool read Get_ParentCtl3D write Set_ParentCtl3D;
property PlainText: WordBool read Get_PlainText write Set_PlainText;
property Readonly: WordBool read Get_ReadOnly write Set_ReadOnly;
property ScrollBars: TxScrollStyle read Get_ScrollBars write Set_ScrollBars;
property Visible: WordBool read Get_Visible write Set_Visible;
property WantTabs: WordBool read Get_WantTabs write Set_WantTabs;
property WantReturns: WordBool read Get_WantReturns write SetJWantReturns;
property Wordwrap: WordBool read Get_WordWrap write Set_WordWrap;
property CanUndo: WordBool read Get_CanUndo;
property Modified: WordBool read Get__Modified write Set_Modified;
property SelLength: Integer read Get_SelLength write Set_SelLength;
property SelStart: Integer read Get_SelStart write Set_SelStart;
property SelText: WideString read Get_SelText write Set_SelText;
property Text: WideString read Get_Text write Set_Text;
property DoubleBuffered: WordBool read Get_DoubleBuffered write Set_DoubleBuffered;
property VisibleDockClientCount: Integer read Get_VisibleDockClientCoxint;
property Cursor: Smallint read Get_Cursor write Set_Cursor;
end;
//***********************************************
// Displntf: IRichEditXDisp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {86F5C46C-CDA6-4279-8BF4-F69F1A051ED7}
//***********************************************
IRichEditXDisp = dispinterface
['{86F5C46C-CDA6-4279-8BF4-F69F1A051ED7}']
property Alignment: TxAlignment dispid 1;
property BorderStyle: TxBorderStyle dispid 2;
property Color: OLE_COLOR dispid -501;
property Ctl3D: WordBool dispid 3;
property DragCursor: Smallint dispid 4;
property DragMode: TxDragMode dispid 5;
property Enabled: WordBool dispid -514;
property Font: IFontDisp dispid -512;
property HideSelection: WordBool dispid 6;
property HideScrollBars: WordBool dispid 7;
property ImeMode: TxImeMode dispid 8;
property ImeName: WideString dispid 9;
property Lines: IStrings dispid 10;
property MaxLength: Integer dispid 11;
property ParentColor: WordBool dispid 12;
property ParentCtl3D: WordBool dispid 13;
property PlainText: WordBool dispid 14;
property Readonly: WordBool dispid 15;
property ScrollBars: TxScrollStyle dispid 16;
property Visible: WordBool dispid 17;
property WantTabs: WordBool dispid 18;
property WantReturns: WordBool dispid 19;
property Wordwrap: WordBool dispid 20;
property CanUndo: WordBool readonly dispid 34;
property Modified: WordBool dispid 35;
property SelLength: Integer dispid 36;
property SelStart: Integer dispid 37;
property SelText: WideString dispid 38;
property Text: WideString dispid -517;
property DoubleBuffered: WordBool dispid 39;
property VisibleDockClientCount: Integer readonly dispid 40;
property Cursor: Smallint dispid 49;
end;
//***********************************************
// Displntf: IRichEditXEvents
// Flags: (0)
// QUID: {21AB5F94-7D87-4FCO-AB5A-9BCE1D05AF8B}
//***********************************************
IRichEditXEvents = dispinterface
['{21AB5F94-7D87-4FCO-AB5A-9BCE1D05AF8B}']
procedure OnChange; dispid 1;
procedure OnKeyPress(var Key: Smallint); dispid 8;
procedure OnProtectChange(StartPos: Integer; EndPos: Integer; var AllowChange: WordBool); dispid 16;
procedure OnSaveClipboardfNumObjects: Integer; NumChars: Integer; var SaveClipboard: WordBool); dispid 18;
procedure OnSelectionChange; dispid 19;
end;

// Объявление класса OLE Control Proxy
// Control Name TRichEditX
// Help String RichEditX Control
// Default Interface IRichEditX
// Def. Intf. DISP? No
// Event Interface IRichEditXEvents
// TypeFlags (34) CanCreate Control
//***********************************************
TRichEditXOnKeyPress = procedure(Sender: TObject; var Key: Smallint) of object;
TRichEditXOnProtectChange = procedure(Sender: TObject; StartPos: Integer; EndPos: Integer;
var AllowChange: WordBool) of object;
TRichEditXOnSaveClipboard = procedure(Sender: TObject; NumObjects: Integer; NumChars-: Integer;
var SaveClipboard: WordBool) of object;
TRichEditX = class(Telecontrol)
private
FOnChange: TNotifyEvent;
FOnKeyPress: TRichEditXOnKeyPress;
FOnProtectChange: TRichEditXOnProtectChange;
FOnSaveClipboard: TRichEditXOnSaveClipboard;
FOnSelectionChange: TNotifyEvent;
FIntf: IRichEditX;
function GetControlInterface: IRichEditX;
protected
procedure CreateControl;
procedure InitControlData; override/function Get_Lines: IStrings;
procedure Set_Lines(const Value: IStrings);
public
property Controllnterface: IRichEditX read GetControlInterface;
property Defaultlnterface: IRichEditX read GetControlInterface;
property CanUndo: WordBool index 34 read GetWordBoolProp;
property Modified: WordBool index 35 read GetWordBoolProp write SetWordBoolProp;
property SelLength: Integer index 36 read GetlntegerProp write SetlntegerProp;
property SelStart: Integer index 37 read GetlntegerProp write SetlntegerProp;
property SelText: WideString index 38 read GetWideStringProp write SetWideStringProp;
property Text: WideString index -517 read GetWideStringProp write SetWideStringProp;
property DoubleBuffered: WordBool index 39 read GetWordBoolProp write SetWordBoolProp;
property VisibleDockClientCount: Integer index 40 read GetlntegerProp;
published
property Alignment: TOleEnum index 1 read GetTOleEnumProp write SetTOleEnumProp stored False;
property BorderStyle: TOleEnum index 2 read GetTOleEnumProp write SetTOleEnumProp stored False;
property Color: TColor index -501 read GetTColorProp write SetTColorProp stored False;
property Ctl3D: WordBool index 3 read GetWordBoolProp write SetWordBoolProp stored False;
property DragCursor: Smallint index 4 read GetSmallintProp write SetSmallintProp stored False property DragMode: TOleEnum index 5 read GetTOleEnumProp write SetTOleEnumProp stored False;
property Enabled: WordBool index -514 read GetWordBoolProp write SetWordBoolProp stored False;
property Font: TFont index -512 read GetTFontProp write SetTFontProp stored False;
property HideSelection; WordBool index 6 read GetWordBoolProp write SetWordBoolProp stored False;
property HideScrollBars: WordBool index 7 read GetWordBoolProp write SetWordBoolProp stored False;
property ImeMode: TOleEnum index 8 read GetTOleEnumProp write SetTOleEnumProp stored False;
property ImeName: WideString index 9 read GetWideStringProp write SetWideStringProp stored False;
property Lines: IStrings read Get_Lines write Set_Lines stored False property MaxLength: Integer index 11 read GetlntegerProp write SetlntegerProp stored False
property ParentColor: WordBool index 12 read GetWordBoolProp write SetWordBoolProp stored False;
property ParentCtlSD: WordBool index 13 read GetWordBoolProp write SetWordBoolProp stored False;
property PlainText: WordBool index 14 read GetWordBoolProp write SetWordBoolProp stored False;
property Readonly: WordBool index 15 read GetWordBoolProp write SetWordBoolProp stored False;
property ScrollBars: TOleEnum index 16 read GetTOleEnumProp write SetTOleEnumProp stored False;
property Visible: WordBool index 17 read GetWordBoolProp write SetWordBoolProp stored False;
property WantTabs: WordBool index 18 read GetWordBoolProp write SetWordBoolProp stored False;
property WantReturns: WordBool index 19 read GetWordBoolProp write SetWordBoolProp stored False;
property Wordwrap: WordBool index 20 read GetWordBoolProp write SetWordBoolProp stored False;
property Cursor: Smallint index 49 read GetSmallintProp write SetSmallintProp stored False;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnKeyPress: TRichEditXOnKeyPress read FOnKeyPress write FOnKeyPress;
property OnProtectChange: TRichEditXOnProtectChange read FOnProtectChange write FOnProtectChange;
property OnSaveClipboard: TRichEditXOnSaveClipboard read FOnSaveClipboard write FOnSaveClipboard;
property OnSelectionChange: TNotifyEvent read FOnSelectionChange write FOnSelectionChange;
end;
procedure Register; implementation uses ComObj;
procedure TRichEditX.InitControlData; const
CEventDispIDs: array [0..4] of DWORD = (
$00000001, $00000008, $00000010, $00000012, $00000013); CTFontlDs: array [0..0] of DWORD = (
$FFFFFEOO); CControlData: TControlData2 = (
ClassID: '{7C731EEO-D98D-4040-8FE1-EOCB9E54C58B}';
EventHD: '{21AB5F94-7D87-4FCO-AB5A-9BCE1D05AF8B}';
EventCount: 5;
EventDispIDs: @CEventDispIDs;
LicenseKey: nil (*HR:$80040154*);
Flags: $00000020;
Version: 401;
FontCount: 1;
FontlDs: @CTFontIDs);
begin
ControlData := @CControlData;
TControlData2(CControlData).FirstEventOfs := Cardinal(SSFOnChange) - Cardinal(Self);
end;
procedure TRichEditX.CreateControl;
procedure DoCreate;
begin
FIntf := lUnknown(OleObject) as IRichEditX;
end;
begin
if FIntf = nil then DoCreate;
end;
function TRichEditX.GetControlInterface: IRichEditX;
begin
CreateControl;
Result := FIntf;
end;
function TRichEditX.Get_Lines: IStrings;
begin
Result := DefaultInterface.Get_Lines;
end;
procedure TRichEditX.Set_Lines(const Value: IStrings);
begin
Defaultlnterface.Set_Lines(Value);
end;
procedure Register;
begin
RegisterComponents('ActiveX',[TRichEditX]);
end;
end.

Как вы можете видеть, приведенные выше листинги имеют достаточно большой размер. Но тот факт, что Delphi берет на себя такую большую работу, позволяет даже непрофессионалу в разработке элементов управления ActiveX создавать свои ActiveX при помощи мастера. Мастер в данном случае генерирует полностью функциональный элемент управления ActiveX с интерфейсами, библиотекой типов и событиями, при этом разработчику не нужно вводить собственный код!
Для установки данного элемента управления в палитру компонентов Delphi нужно сделать следующее:
1. Откомпилировать проект с помощью пункта главного меню Project/Compile <имя проекта> (Проект/Компилировать <имя проекта>), при этом будет создана библиотека OCX.
2. С помощью пункта главного меню Component/Import ActiveX Control (Компонент/Импорт элемента управления ActiveX) открыть окно импорта нового элемента ActiveX.
3. В диалоговом окне импорта ActiveX нажать кнопку Add (Добавить), перейти в каталог, содержащий созданный файл OCX, и выбрать его двойным щелчком, элемент управления появится в списке диалогового окна импорта ActiveX.
4. Выбрать данный элемент управления в списке и нажать кнопку Install (Установить).
5. Выбрать файл пакета и нажать кнопку ОК.
После выполнения данной последовательности шагов в палитре компонентов Delphi появится ваш элемент управления ActiveX.
Следует заметить, что создание элементов управления ActiveX из стандартных компонентов Delphi применяется очень редко. Вы можете использовать данную процедуру преобразования компонента в ActiveX к самостоятельно созданному компоненту, что значительно расширит его функциональность и позволит использовать ваш компонент разработчиками в других средах программирования.

ActiveX в Delphi

Добавление свойств и методов

Добавление свойств и методов в ваш MTS-объект ничем не отличается от добавления свойств и методов в любой другой СОМ-объект.

Использование мастера MTS-объекта

Для запуска мастера выполните:
1. Выберите в главном меню пункт File/New (Файл/Новый).
2. В появившемся диалоговом окне выберите вкладку Multitier.
3. Дважды щелкните на пиктограмме MTS Object (рис. 3.34).
В результате выполнения приведенных выше шагов запустится мастер создания MTS-объекта (рис. 3.35).
В поле CoClass Name (Имя со-класса) введите имя для вашего MTS-класса, например MуMTS. В выпадающем списке Threading Model (Модель потока) выберите тип потоковой модели для вашего MTS-объекта (потоковые модели были рассмотрены в предыдущих главах). Далее выберите модель транзакции, установив нужный переключатель Transaction Model (Модель транзакции).
Остановимся на моделях транзакции более подробно. Итак, каждый MTS-объект должен иметь атрибут транзакции, который записывается в MTS- каталог. MTS-каталог, кроме того, содержит информацию о компонентах, пакетах и ролях. Разработчик может управлять MTS-каталогом при помощи MTS -обозревателя.

Использование мастера MTS-объекта

Рис. 3.34. Создание нового MTS-объекта

Использование мастера MTS-объекта

Рис. 3.35. Мастер создания MTS-объекта
Имеются четыре значения, которые может принимать атрибут транзакции:
- Requires a transaction (Требует транзакцию). MTS-объект должен выполняться внутри области транзакции. При создании нового MTS-объекта объект наследует транзакцию от клиента. Если клиент не содержит транзакций, MTS автоматически создает транзакцию для MTS-объекта;
- Requires a new transaction (Требует новую транзакцию). MTS-объект должен выполняться внутри собственной транзакции. При создании нового
объекта MTS автоматически создает новую транзакцию для MTS-объекта, независимо от того, имеет ли клиент транзакции. Объект никогда не работает внутри области транзакций клиента. Система всегда создает независимые транзакции для новых объектов;
- Supports transactions (Поддерживает транзакции). MTS-объект может выполняться внутри области транзакций клиента. При создании нового объекта, объект наследует транзакции клиента. Если клиент не имеет транзакций, MTS-объект будет создан также без транзакций;
- Does not support transactions (He поддерживает транзакции). MTS-объект не работает в области транзакций. При создании нового объекта объект создается без транзакций, независимо от того, имеет ли клиент транзакции. Используйте данное значение атрибута транзакций для создания СОМ-объектов, обеспечивающих поддержку MTS.
Опция Generate Event support code (Создавать код поддержки событий) устанавливается, если вы хотите использовать собственный интерфейс для управления событиями вашего MTS-объекта.
После нажатия кнопки ОК в окне мастера создания MTS-объекта к вашему проекту будет добавлен новый модуль, который содержит объявление нового MTS-объекта (листинг 3.13).

Листинг 3.13
uses
ActiveX, MtsObj, Mtx, ComObj, Project2_TLB, StdVcl;
type
TMyMTS = class(TMtsAutoObject, IMyMTS)
protected
{ Protected declarations }
end;
implementation
uses ComServ;
initialization
TAutoObjectFactory.Create(ComServer, TMyMTS, Class_MyMTS, ciMultilnstance, tmApartment);
end.

В дополнение, мастер допишет библиотеку типов к проекту и откроет редактор библиотеки типов (рис. 3.36).

Использование мастера MTS-объекта

Рис. 3.36. Редактор библиотеки типов и MTS-объект

MTS-компоненты

MTS-компоненты обеспечивают следующие услуги низкого уровня:
- управление системными ресурсами, включая процессы, потоки и поддержку одновременного подключения к базе данных большого числа пользователей;
- автоматическое управление транзакциями;
- создание, выполнение и удаление компонентов сервера;
- обеспечение безопасности вашего приложения с помощью авторизации по ролям.
Delphi предоставляет возможность использовать в качестве клиентов MTS произвольные приложения или активные формы (ActiveForms).
MTS-компоненты - это СОМ-серверы внутри процесса, которые содержатся внутри библиотек DLL. Они отличаются от других СОМ-компонентов тем, что выполняются в специальной MTS-среде. MTS-компоненты реализованы в Delphi так же, как и остальные классы.
Обычно, MTS-серверы являются небольшими объектами и используются для различных бизнес-функций. Например, MTS-компоненты могут обеспечивать бизнес-правила приложения, а также различные виды и превращения состояний приложения. Рассмотрим, например, типичное приложение, которое может использоваться в медицинских учреждениях. Как известно, различные записи баз данных хранят сведения о пациентах, их истории болезней, группы крови и многое другое. MTS-компонент позволяет в реальном времени обновлять те изменения, которые произошли. Например, был добавлен новый пациент, введены результаты анализов и т. д.
Как показано на рис. 3.33, MTS-объект может рассматриваться как любой другой СОМ-объект. Но в дополнении к СОМ-интерфейсам MTS-объект поддерживает собственные MTS-интерфейсы.

MTS-компоненты

Рис. 3.33. Простой MTS-объект
На рис. 3.33, кроме СОМ-интерфейсов, отдельно показан интерфейс IObjectControl. Для всех СОМ-объектов общим интерфейсом является IUnknown, а для всех MTS-объектов - интерфейс Iobjectcontrol. Данный интерфейс содержит методы для активации и завершения работы MTS-объектов, а также для управления ресурсами.
Клиент сервера, находящийся внутри MTS-среды, называется базовым клиентом. С точки зрения базового клиента, СОМ-объект внутри MTS-среды выглядит так же, как любой другой СОМ-объект. MTS-объекты размещаются в DLL, которые подключаются к исполняемому (ЕХЕ) файлу MTS.
По своей работе MTS-объекты очень похожи на обычные СОМ-объекты, которые мы рассматривали в предыдущих главах.

Создание MTS-объекта с помощью мастера

Для создания простого MTS-объекта вам необходимо выполнить следующие шаги:
1. Использовать мастер MTS-объекта для создания MTS-компонента.
2. Добавить свойства и методы при помощи редактора библиотеки типов.
3. Протестировать и отладить ваш MTS-компонент.
4. Установить MTS-компонент в существующий или новый пакет.
5. Управлять средой MTS при помощи MTS-проводника (MTS Explorer).

Требования, предъявляемые к MTS-компонентам

Кроме требований, которые предъявляются ко всем СОМ-объектам, MTS-компоненты должны удовлетворять следующим требованиям:
- MTS-компонент, при создании с помощью мастера, должен иметь стандартную фабрику класса, которая автоматически поддерживается Delphi;
- MTS-компонент должен экспортировать стандартный метод
DllGetClassObject;
- все интерфейсы и со-классы компонента должны быть описаны в библиотеке типов, которая также создается мастером MTS-объекта;
- MTS-компоненты должны экспортировать интерфейсы, использующие стандартный СОМ-маршалинг;
- компоненты должны экспортировать функцию DllRegisterServer и обеспечивать самостоятельную регистрацию CLSID, ProgID, интерфейсов и библиотеки типов;
- компонент, запущенный в процессе MTS, не может взаимодействовать с компонентами, не запущенными в MTS.

Установка MTS-объекта в MTS-пакет

Для установки MTS-объекта в MTS-пакет вам нужно выполнить следующие шаги:
1. Выбрать пункт главного меню Run/Install MTS Object (Запуск/Установить MTS-объект).
2. Убедиться в том, что MTS-объект был установлен.

Глава 13 Содержание

ActiveX в Delphi

Сообщения об ошибках и исключениях

Список исключительных ситуаций мы уже обсуждали во второй главе книги. В данном приложении мы рассмотрим системные ошибки Win32 API. Код такой ошибки можно получить при помощи вызова функции GetLastError. В приведенной ниже таблице представлен список констант, получаемых в результате вызова функции GetLastError и их значений, а также краткое описание ошибок.
Таблица П. 1. Коды ошибок Win32 API
Константа

Значение

Описание

ERROR_SUCCESS

0

Операция выполнена успешно

ERROR_INVALID_FUNCTION

1

Неверная функция

ERROR_FILE_NOT_FOUND

2

Не найден требуемый файл

ERROR_PATH_NOT_FOUND

3

Не найден указанный путь

ERROR_TOO_MANY_OPEN_FILES

4

Не удается открыть файл

ERROR_ACCESS_DENIED

5

Нет доступа

ERROR_INVALID_HANDLE

6

Неверный дескриптор

ERROR_ARENA_TRASHED

7

Повреждены управляющие блоки памяти

ERROR_NOT_ENOUGH_MEMORY

8

Недостаточно памяти для обработки команды

ERROR_INVALID_BLOCK

9

Неверный адрес управляющего блока памяти

ERROR_BAD_ENVIRONMENT

10

Ошибка в среде

ERROR_BAD_FORMAT

11

Неверный формат файла

ERROR_INVALID_ACCESS

12

Неверный код доступа

ERROR_INVALID_DATA

13

Ошибка в данных

ERROR_OUTOFMEMORY

14

Недостаточно памяти для завершения операции

ERROR_INVALID_DRIVE

15

Не удается найти указанный диск

ERROR_CURRENT_DIRECTORY

16

Не удается удалить папку

ERROR_NOT_SAME_DEVICE

17

Не удается переместить файл на другой диск

ERROR_NO_MORE_FILES

18

Файлов больше нет

ERROR_WRITE_PROTECT

19

Диск защищен от записи

ERROR_BAD_UNIT

20

Не удается найти указанное устройство

ERROR_NOT_READY

21

Устройство не готово

ERROR_BAD_COMMAND

22

Устройство не опознает команду

ERROR_CRC

23

Ошибка в данных (CRC)

ERROR_BAD_LENGTH

24

Большая длина выданной программой команды

ERROR_SEEK

25

Не удается найти заданную дорожку или область на диске

ERROR_NOT_DOS_DISK

26

Нет доступа к диску

ERROR_SECTOR_NOT_FOUND

27

Не удается найти заданный сектор на диске

ERROR_OUT_OF_PAPER

28

Нет бумаги в принтере

ERROR_WRITE_FAULT

29

Не удается произвести запись на устройство

ERROR_READ_FAULT

30

Не удается произвести чтение с устройства

ERROR_GEN_FAILURE

31

Присоединенное к системе устройство не работает

ERROR_SHARING_VIOLATION

32

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

ERROR_LOCK_VIOLATION

33

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

ERROR_WRONG_DISK

34

В устройство вставлен неверный диск

ERROR_SHARING_BUFFER_EXCEEDED

36

Слишком много файлов открыто для совместного пользования

ERROR_HANDLE_EOF

38

Достигнут конец файла

ERROR_HANDLE_DISK_FULL

39

Диск полностью заполнен

ERROR_NOT_SUPPORTED

50

Сетевой запрос не поддерживается

ERROR_REM_NOT_LIST

51

Удаленный компьютер не доступен

ERROR_DUP_NAME

52

В сети присутствуют совпадающие имена

ERROR_BAD_NETPATH

53

Не найден сетевой путь

ERROR_NETWORK_BUSY

54

Сеть занята

ERROR_DEV_NOT_EXIST

55

Сетевой ресурс или устройство более недоступно

ERROR_TOO_MANY_CMDS

56

Достигнут предел числа команд NetBIOS

ERROR_ADAP_HDW_ERR

57

Аппаратная ошибка сетевой платы

ERROR_BAD_NET_RESP

58

Указанный сервер не может выполнить требуемую операцию

ERROR_UNEXP_NET_ERR

59

Неожиданная ошибка в сети

ERROR_BAD_REM_ADAP

60

Несовместимый удаленный адаптер

ERROR_PRINTQ_FULL

61

Очередь печати переполнена

ERROR_NO_SPOOL_SPACE

62

На сервере нет места для записи файла, выводимого на печать

ERROR_PRINT_CANCELLED

63

Был отменен вывод на печать файла, находящегося в очереди

ERROR_NETNAME_DELETED

64

Указанное сетевое имя больше недоступно

ERROR_NETWORK_ACCES_DENIED

65

Отсутствует доступ к сети

ERROR_BAD_DEV_TYPE

66

Неправильно указан тип сетевого ресурса

ERROR_BAD_NET_NAME

67

Не найдено сетевое имя

ERROR_TOO_MANY_NAMES

68

Превышено число имен для сетевой платы компьютера

ERROR_TOO_MANY_SESS

69

Превышено допустимое число сеансов NetBIOS

ERROR_SHARING_PAUSED

.70

Сервер сети был остановлен или перезапускается

ERROR_REQ_NOT_ACCEP

71

Невозможно подключиться к удаленному компьютеру, т. к. достигнуто предельное число подключений

ERROR_REDIR_PAUSED

72

Работа указанного устройства была приостановлена

ERROR_FILE_EXISTS

80

Файл существует

ERROR_CANNOT_MAKE

82

Не удается создать файл или папку

ERROR_FAIL_I24

83

Ошибка при обращении к прерыванию INT 24

ERROR_OUT_OF_STRUCTRURES

84

Недостаточно памяти для обработки запроса

ERROR_ALREADY_ASSIGNED

85

Имя устройства уже используется

ERROR_INVALID_PASSWORD

86

Неправильно указан сетевой пароль

ERROR_INVALID_PARAMETER

87

Неверное задание параметра .

ERROR_NET_WRITE_FAULT

88

Ошибка записи в сеть

ERROR_NO_PROC_SLOTS

89

Невозможно запустить другой процесс

ERROR_TOO_MANY_SEMAPHORES

100

Не удается создать еще один системный семафор

ERROR_EXCL_SEM_ALREADY_OWNED

101

Семафор эксклюзивного доступа занят другим процессом

ERROR_SEM_I S_SET

102

Семафор установлен и не может быть закрыт

ERROR TOO_MANY_SEM_REQUESTS

103

Семафор не может быть установлен повторно

ERROR_INVALID_AT_INTERRUPT_TIME

104

Нельзя послать запрос к семафору эксклюзивного доступа во время выполнения прерываний

ERROR_SEM_OWNER_DIED

105

Данный семафор больше не принадлежит использовавшему его процессу

ERROR_SEM_USER_LIMIT

106

Достигнут предел числа пользователей семафора

ERROR_DI SK_CHANGE

107

В устройстве установлен не тот диск

ERROR_DRIVE_LOCKED

108

Диск занят или заблокирован

ERROR_BROKEN_PI PE

109

Канал закрыт

ERROR_OPEN_FAILED

110

Не удается открыть указанное устройство или файл

ERROR_BUFFER_OVERFLOW

111

Переполнение буфера

ERROR_DISK_FULL

112

Отсутствует дисковое пространство

ERROR_NO_MORE_SEARCH_HANDLES

113

Больше нет внутренних идентификаторов файлов

ERROR_INVALID_TARGET_HANDLE

114

Неправильный внутренний идентификатор файла

ERROR_INVALID_CATEGORY

117

Неправильный вызов функции

ERROR_INVALI D_VERI FY_SWITCH

118

Неправильное значение параметра проверки записи данных на диск

ERROR_BAD_DRIVER_LEVEL

119

Невозможна обработка полученной команды

ERROR_CALL_NOT_IMPLEMENTED

120

Вызов функции не поддерживается в Win 16

ERROR_SEM_TIMEOUT

121

Истекло время ожидания семафора

ERROR_INSUFFICIENT_BUFFER

122

Недостаточный объем области данных

ERROR_INVALID_NAME

123

Неправильное имя файла, папки или метки тома

ERROR_INVALID LEVEL

124

Неверный уровень системного вызова

ERROR_NO_VOLUME_LABEL

125

Диск не имеет метки тома

ERROR_MOD_NOT_FOUND

126

Не найден указанный модуль

ERROR_PROC_NOT_FOUND

127

Не найдена указанная процедура

ERROR_WAIT_NO_CHILDREN

128

Нет дочерних процессов, окончания работы которых требуется ожидать

ERROR_CHILD_NOT_COMPLETE

129

Выполнение дочернего процесса не завершено

ERROR_DIRECT_ACCESS_HANDLE

130

Попытка использования дескриптора файла для открытия диска и выполнения операции, отличающейся от ввода/вывода

ERROR_NEGATIVE_SEEK

131

Попытка поместить указатель на файл перед файлом

ERROR_SEEK_ON_DEVICE

132

Указатель на файл не может быть установлен на заданный файл

ERROR_I S_ JOIN_TARGET

133

Команда JOIN не может быть использована для дисков, в которых содержатся объединенные диски

ERROR_IS_ JOINED

134

Попытка использовать команду JOIN или SUBST для диска, который уже включен в набор объединенных дисков

ERROR_IS_SUBSTED

135

Попытка использовать команду JOIN или SUBST для диска, который уже был отображен

ERROR_NOT_JOINED

136

Диск не был объединен

ERROR_NOT_SUBSTED

137

Невозможно снять признак отображения с диска, который не был отображен

ERROR_JOIN TO_JOIN

138

Невозможно объединить диск с папкой на объединенном диске

ERROR_SUBST_TO_SUBST

139

Невозможно отобразить диск на папку, находящуюся на отображенном диске

ERROR_JOIN_TO_SUBST

140

Невозможно объединить диск с папкой, находящейся на отображенном диске

ERROR_SUBST_TO_JOIN

141

Невозможно отобразить диск на папку, находящуюся на объединенном диске

ERROR_BUSY_DRIVE

142

Диск занят, невозможно выполнить команду JOIN или SUBST

ERROR_SAME_DRIVE

143

Попытка объединения или отображения одного и того же диска

ERROR_DIR_NOT_ROOT

144

Папка не является корневой

ERROR_DIR_NOT_EMPTY

145

Папка не пуста

ERROR_IS_SUBST_PATH

146

Указанный путь используется для отображенного диска

ERROR_IS_JOIN_PATH

147

Указанный путь используется для объединенного диска

EKROR_PATH_BUSY

148

Невозможно использовать указанный путь

ERROR_I S_SUBST_TARGET

149

Невозможно объединить или отобразить диск, т. к. его папка уже используется для отображения

ERROR_SYSTEM_TRACE

150

Трассировка запрещена

ERROR_INVALID_EVENT_COUNT

151

Неправильно задано количество семафоров для процедуры DosMuxSemWait

ERROR_TOO_MANY_MUXWAITERS

152

Задано слишком большое число семафоров для процедуры DosMuxSemWait

ERROR_INVALID_LIST_FORMAT

153

Некорректный вызов процедуры DosMuxSemWait

ERROR_LABEL_TOO_LONG

154

Слишком длинная метка тома для диска

ERROR_TOO_MANY TCBS

155

Превышен предел потоков команд

ERROR_SIGNAL_REFUSED

156

Сигнал был отклонен принимающим процессом

ERROR_DISCARDED

157

Невозможно заблокировать освобожденный сегмент

ERROR_NOT_LOCKED

158

Сегмент не заблокирован

ERROR_BAD_THREADID ADDR

159

Неправильно задан адрес идентификатора потока команд

ERROR_BAD_ARGUMENTS

160

Передан неправильный аргумент для процедуры

DosExecPrgm

ERROR_BAD_PATHNAME

161

Неправильно задан путь

ERROR_SIGNAL_PENDING

162

Сигнал обрабатывается

ERROR_MAX_THRDS_REACHED

164

Невозможно создать дополнительные потоки команд

ERROR_LOCK_FAILED

167

Невозможно снять блокировку с области файла

ERROR_BUSY

170

Запрашиваемый ресурс занят

ERROR_CANCEL_VIOLATION

173

Запрос на блокировку соответствует определенной области

ERROR_ATOMIC_LOCKS_NOT_SUPPORTED

174

Файловая система не поддерживает данное изменение типа блокировки

ERROR_INVALID_SEGMENT_NUMBER

180

Неверный номер сегмента

ERROR_INVALID_ORDINAL

182

Неправильный порядок

ERROR_ALREADY_EXIST

183

Файл уже существует

ERROR_INVALID_FLAG_NUMBER

186

Неправильный номер флага

ERROR_SEM_NOT_FOUND

187

Семафор не найден

ERROR_INVALID_STARTING_CODESEG

188

Неправильно указана стартовая кодовая страница

ERROR_INVALID_STACKSEG

189

Неправильный сегмент стека

ERROR_INVALID_MODULETYPE

190

Неправильный тип модуля

ERROR_INVALID_EXE_SIGNATURE

191

Неправильное имя ЕХЕ-файла

ERROR EXE_MARKED_INVALID

192

Неправильная маркировка ЕХЕ-файла

ERROR_BAD_EXE_FORMAT

193

Неправильный формат ЕХЕ-файла

ERROR_ITERATED_DATA_EXCEEDS_64K

194

Размер блока данных не должен превышать 64 Кбайт

ERROR_INVALI D_MINALLOCS I ZE

195

Неверный минимальный размер файла

ERROR_DYNLINK_FROM_INVALID_RING

196

Неправильная динамическая ссылка

ERROR_IOPL_NOT_ENABLED

197

Невозможно запустить приложение в данной операционной системе

ERROR_INVALID_SEGDPL

198

Неправильный сегмент

ERROR_AUTODATASEG_EXCEEDS_64K

199

Сегмент кода не может превышать 64 Кбайт

ERROR_ENWAR_NOT_FOUND

203

Не удается найти указанный параметр

ERROR_NOSIGNAL_SENT

205

Ни один процесс не может обработать сигнал

ERROR_FILENAME_EXCED_RANGE

206

Превышена длина имени или расширения файла

ERROR_RING2_STACK_IN_USE

207

Стек занят

ERROR_MATA EXPANSION TOO_LONG

208

Неверно задан шаблон имени файла

ERROR_INVALID_SIGNAL_NUMBER

209

Неверный сигнал

ERROR_THREAD_1_INACT I VE

210

Не удается установить обработчик сигналов

ERROR_LOCKED

212

Сегмент заблокирован, перемещение невозможно

ERROR_TOO_MANY_MODULES

214

Превышено максимальное число подключаемых модулей к данной программе или модулю

ERROR_NESTING NOT ALLOWED

215

Вызовы процедуры LoadModule не могут быть вложенными

ERRORJBAD PIPE

230

Неправильное состояние канала

ERROR_PI PE_BUS Y

231

Канал занят

ERROR NO DATA

232

Идет процесс закрытия канала

ERROR_PIPE_NOT_CONNECTED

233

Канал не подключен ни к одному процессу

ERROR_MORE_DATA

234

Необходимы дополнительные данные

ERROR_VC_DI SCONNECTED

240

Сеанс был прекращен

ERROR_INVALID_EA_NAME

254

Неверное задание имени дополнительного атрибута

ERROR_EA_LI ST_INCONS I STENT

255

Дополнительные атрибуты не совместимы между собой

ERROR__NO_MORE_ITEMS

259

Отсутствие дополнительных данных

ERROR_CANNOT_COPY

266

Невозможно использовать интерфейс API Copy

ERROR_DIRECTORY

267

Неверно задано имя папки

ERROR_EAS_DIDN' T_FIT

275

Дополнительные атрибуты не помещаются в буфере

ERROR_EA_FI LE_CORRUPT

276

Файл дополнительных атрибутов поврежден

ERROR_EA_TABLE_FULL

277

Файл дополнительных атрибутов переполнен

ERROR_INVALID_EA_HANDLE

278

Неправильно указан дескриптор дополнительного атрибута

ERROR_EAS_NOT_SUPPORTED

282

Файловая система не поддерживает дополнительные атрибуты

ERRORJTOT OWNER

288

Освобождение не принадлежащего процессу объекта синхронизации

ERROR_TOO MANY POSTS

298

Слишком много попыток занесения события для семафора

ERROR_PART I AL_COPY

299

Запрос записи/чтения в процесс был выполнен лишь частично

ERROR_MR_MID_NOT_FOUND

317

Не удается найти данное сообщение в файле сообщений

ERROR INVALID ADDRESS

487

Неверный адрес

ERROR ARITHMETIC_OVERFLOW

534

Результат арифметической операции превысил 32 разряда

ERROR_PIPE_CONNECTED

535

С другой стороны канала уже присутствует процесс

ERROR_PIPE_LISTENING

536

Идет ожидание открытия процессом другой стороны канала

ERROR_EA_ACCESS_DENIED

994

Нет доступа к дополнительным атрибутам

ERROR_OPERATION_ABORTED

995

Операция ввода/вывода была прервана

ERROR_IO_JENCOMPLETE

996

Операция ввода/вывода не завершена

ERROR_IO_PENDING

997

Происходит событие ввода/вывода

ERROR NOACCESS

998

Неверная попытка доступа к адресу памяти

ERROR_SWAPERROR

999

Ошибка выполнения операции со страницей

ERROR_STACK_OVERFLOW

1001

Переполнение стека

ERROR_INVALID_MESSAGE

1002

Невозможно обработать сообщение

ERROR_CAN_NOT_COMPLETE

1003

Невозможно завершить выполнение функции

ERROR INVALID FLAGS

1004

Флаги установлены неверно

ERROR_UNRECOGNIZED_VOLUME

1005

Не удается определить, какая файловая система установлена на данном томе

ERROR_FILE_INVALID

1006

Работа с файлом невозможна

ERROR_FULLSCREEN_MODE

1007

Нельзя выполнить данную операцию в полноэкранном режиме

ERROR_NO_TOKEN

1008

Элемент не существует

ERROR_BADDB

1009

База данных реестра повреждена

ERROR_BADKEY

1010

Параметр реестра имеет неверное значение

ERROR_CANTOPEN

1011

Не удается открыть параметр реестра

ERROR_CANTREAD

1012

Не удается прочитать параметр реестра

ERROR_CANTWRITE

1013

Не удается записать параметр реестра

ERROR_REGISTRY_RECOVERED

1014

Восстановление одного из файлов реестра прошло успешно

ERROR_REGI STRY_CORRUPT

1015

Реестр поврежден

ERROR_REGISTRY_IO_FAILED

1016

Не удалось выполнить чтение или запись для одного из файлов, содержащих образ системного реестра

ERROR_NOT_REGISTRY_FILE

1017

Не обнаружен файл реестра, либо он имеет неверный формат

ERROR_KEY_DELETED

1018

Параметр реестра был помечен на удаление

ERROR_NO_LOG_S PACE

1019

Не удалось выделить место в протоколе реестра

ERROR KEY HAS CHILDREN

1020

Невозможно создание символической связи для параметра реестра, который уже содержит подпараметры или значения

ERROR CHILD_MUST_BE_VOLATILE

1021

Нельзя создать статический подпараметр для временного родительского параметра

ERROR_NOTIFY_ENUM_DIR

1022

Данные не были возвращены в буфер вызывающей процедуры

ERROR DEPENDENT SERVICES RUNNING

1051

Команда остановки была отправлена службе, от которой зависят другие службы

ERROR_INVALID_SERVICE_CONTROL

1052

Недопустимая команда для данной службы

ERROR_SERVICE_REQUEST_TIMEOUT

1053

Время ожидания ответа от службы истекло

ERROR SERVICE_NO_THREAD

1054

Не удалось создать поток команд для службы

ERROR_SERVICE_DATABASE_LOCKED

1055

База данных службы заблокирована

ERROR_SERVICE_ALREADY_RUNNING

1056

Одна копия службы уже была запущена

ERROR_INVALID_SERVICE_ACCOUNT

1057

Имя учетной записи задано неверно или не существует

ERROR SERVICE_DISABLED

1058

Указанная служба отключена

ERROR_CIRCULAR_DEPENDENCY

1059

Нельзя установить циклическую зависимость между службами

ERROR_SERVICE_DOES_NOT_EXIST

1060

Указанная служба не установлена

ERROR_SERVICE_CANNOT_ACCEPT_CTRL

1061

Служба не может принимать команды

ERROR SERVICE_NOT_ACTIVE

1062

Служба не была запущена

ERROR FAILED_SERVICE_CONTROLLER

1063

Процесс службы не может установить связь с контроллером службы

ERROR_EXCEPTION_IN_SERVICE

1064

Ошибка службы при обработке команды

ERROR_DATABASE_DOES_NOT_EXIST

1065

Указанная база данных не существует

ERROR_SERVICE_SPECIFIC_ERROR

1066

Служба возвратила код специфической ошибки

ERROR_PROCESS_ABORTED

1067

Процесс был неожиданно завершен

ERROR_SERVICE_DEPENDENCY_FAIL

1068

Невозможно запустить дочернюю службу

ERROR_SERVICE_LOGON_FAILED

1069

Служба не запущена из-за сбоя при входе

ERROR_SERVICE_START_HANG

1070

Служба зависла после запуска

ERROR_INVALID_SERVICE_LOCK

1071

Неверно выполнена блокировка базы данных указанной службы

ERROR_SERVICE_MARKED_FOR_DELETE

1072

Указанная служба была помечена на удаление

ERROR_SERVICE_EXISTS

1073

Указанная служба уже существует

ERROR_ALREADY_RUNNING_LKG

1074

Система работает с использованием последней корректной конфигурации (Last known good)

ERROR_SERVICE_DEPENDENCY_DELETED

1075

Дочерняя служба не существует или была помечена на удаление

ERROR_BOOT_ALREADY_ACCEPTED

1076

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

ERROR_SERVICE_NEVER_STARTED

1077

Попытки запустить службу не наблюдались

ERROR_DUPLICATE_SERVICE_NAME

1078

Такое имя службы уже существует

ERROR_END_OF_MEDIA

1100

Достигнут физический конец ленты стримера

ERROR_FILEMARK_DETECTED

1101

Достигнута метка файла

ERROR_BEGINNING_OF_MEDIA

1102

Достигнуто начало раздела ленты стримера

ERROR_SETMARK_DETECTED

1103

Достигнут конец набора файлов

ERROR_NO_DATA DETECTED

1104

На ленте стримера больше нет данных

ERROR_PARTITION_FAILURE

1105

He удается создать разделы на ленте стримера

ERROR_INVALI D_BLOCK_LENGTH

1106

Неверный текущий размер блока при обращении к новой магнитной ленте из многотомного раздела

ERROR_DEVICE_NOT_PARTITIONED

1107

Не найдены сведения о разделах данной магнитной ленты

ERROR_UNABLE_TO_LOCK_MEDIA

1108

Не удается заблокировать механизм извлечения ленты

ERROR_UNABLE_TO_UNLOAD_MEDIA

1109

Не удается извлечь магнитную ленту из устройства

ERROR_MEDIA_CHANGED

1110

Лента в устройстве была заменена

ERROR_BUS_RESET

1111

Шина ввода/вывода была заново инициализирована

ERROR NO MEDIA IN DRIVE

1112

Нет ленты в стримере

ERROR_NO_UNICODE_TRANSLATION

1113

Невозможно перекодировать символ Unicode

ERROR_DLL_INIT_FAILED

1114

Сбой в программе инициализации DLL

ERROR_SHUTDOWN_IN_PROGRESS

1115

Идет процесс завершения работы системы

ERROR_NO_SHUTDOWN_IN_PROGRESS

1116

Невозможно прервать завершение работы системы, т. к. оно не было инициализировано

ERROR 10 DEVICE

1117

Ошибка ввода/вывода

ERROR_SERIAL_NO_DEVICE

1118

Последовательные устройства не инициализированы

ERROR_IRQJ3USY

1119

Данный запрос на прерывание (IRQ) уже используется другим устройством

ERROR MORE WRITES

1120

Последовательная операция ввода/вывода была завершена в результате операции записи в последовательный порт

ERROR COUNTER TIMEOUT

1121

Последовательная операция ввода/вывода была завершена по истечении периода ожидания

ERROR FLOPPY ID MARK NOT FOUND

1122

На гибком диске не обнаружена адресная метка идентификатора

ERROR FLOPPY WRONG CYLINDER

1123

Несоответствие между полем идентификатора сектора гибкого диска и адресом дорожки контроллера

ERROR_FLOPPY_UNKNOWN_ERROR

1124

Неизвестная ошибка, возвращаемая контроллером гибких дисков

ERROR_FLOPPY_BAD_REGISTERS

1125

Некорректные значения регистров, возвращенные контроллером гибких дисков

ERROR_DISK_RECALIBRATE_FAILED

1126

Многократный сбой операции проверки при обращении к жесткому диску

ERROR DISK OPERATION FAILED

1127

Многократный сбой операции при обращении к жесткому диску

ERROR_DISK_RESET_FAILED

1128

Не удалось выполнение операции сброса контроллера жесткого диска

ERROR_EOM_OVERFLOW

1129

Достигнут физический конец устройства

ERROR_NOT^ENOUGH_SERVER_MEMORY

\

1130

Недостаточно памяти сервера для обработки команды

ERROR POSSIBLE DEADLOCK

1131

Обнаружена вероятность возникновения взаимоблокировки

ERROR MAPPED ALIGNMENT

1132

Базовый адрес или смещение имеют неверное выравнивание

ERROR_SET_POWER_STATE_VETOED

1140

Попытка изменения режима питания была заблокирована другим приложением

ERROR_SET_POWER_STATE_FAILED

1141

Сбой BIOS при попытке изменения режима питания

ERROR_OLD_WIN_VERSION

1150

Для данной программы требуется более поздняя версия Windows

ERROR_APP_WRONG_OS

115,1

Данная программа не является приложением Windows или MS-DOS

ERROR_SINGLE_INSTANCE_APP

1152

Невозможен запуск более одной копии данной программы

ERROR_RMODE_APP

1153

Программа использует реальный режим

ERROR_INVALI D_DLL

1154

Поврежден один из файлов DLL, необходимых для работы данного приложения

ERROR_NO_ASSOTIATION

1155

Указанному файлу не сопоставлено ни одно приложение для выполнения данной операции

ERROR_DDE_FAIL

1156

Ошибка при пересылке команды приложению

ERROR_DLL_NOT_FOUND

1157

Не найден один из файлов DLL, необходимых для работы данного приложения

ERROR_BAD_DEVI CE

1200

Неверное имя устройства

ERROR_CONNECTION_UNAVAIL

1201

Устройство не присоединено

ERROR_DEVICE_ALREADY_REMEMBERED

1202

Попытка записи сведений об устройстве, которые уже были записаны

ERROR_NO_NET_OR_BAD_PATH

1203

Нет подключения к сети или не правильно задан сетевой путь

ERROR_BAD_PROVIDER

1204

Имя системы доступа к сети задано неверно

ERROR_CANNOT_OPEN_PROFILE

1205

Не удается открыть конфигурацию подключения к сети

ERROR_BAD_PROFILE

1206

Конфигурация подключения к сети повреждена

ERROR_NOT_CONTAINER

1207

Перечисление для объектов, которые не являются контейнерами, запрещено

ERROR_EXTENDED_ERROR

1208

Ошибка

ERROR_INVALID_GROUPNAME

1209

Неверный формат имени группы

ERROR_INVALID_COMPUTERNAME

1210

Неверный формат имени компьютера

ERROR INVALID EVENTNAME

1211

Неверный формат имени события

ERROR_INVALID_DOMAINNAME

1212

Неверный формат имени домена

ERROR_INVALID SERVICENAME

1213

Неверный формат имени службы

ERROR_INVALID_NETNAME

1214

Неверный формат сетевого имени

ERROR_INVALID. SHARENAME

1215

Неверный формат имени разделяемого ресурса

ERROR_INVALID PASSWORDNAME

' 1216

Неверный формат пароля

ERROR_INVALID_MESSAGENAME

1217

Неверный формат имени сообщения

ERROR_INVALID_MESSAGEDEST

1218

Неверный формат задания адреса, по которому отправляется сообщение

ERROR__SESSION_CREDENTIAL_CONFLIC

1219

Конфликт между указанными и существующими личными данными

ERROR_REMOTE_SESSION_LIMIT_EXCEE

1220

На сервере открыто слишком много сеансов

ERROR__DUP_DOMAINNAME

1221

Имя рабочей группы или домена уже существует и используется другим компьютером в сети

ERROR_NO_NETWORK

1222

Сеть отсутствует

ERROR_CANCELLED

1223

Операция отменена пользователем

ERROR_USER_MAPPED_FILE

1224

Операция не может быть выполнена для файла с открытым разделом

ERROR_CONNECTION_RE FUSED

1225

Удаленная система отклонила запрос на подключение к сети

ERROR GRACEFUL_DISCONNECT

1226

Сетевое подключение было закрыто

ERROR ADDRESS ALREADY_ASSOCIATED

1227

Конечной точке сетевого транспорта уже сопоставлен адрес

ERROR_ADDRES_NOT_ASSOCIATED

1228

Конечной точке сетевого транспорта еще не был сопоставлен адрес

ERROR_CONNECTION_INVALID

1229

Ошибка выполнения операции для несуществующего сетевого подключения

ERROR CONNECTION_ACTIVE

1230

Недопустимая операция для активного сетевого подключения

ERROR_NETWORK_UNREACHABLE

1231

Удаленная сеть недоступна

ERROR_HOST_UNREACHABLE

1232

Данный транспорт не обеспечивает доступ к удаленной системе

ERROR_PROTOCOL_UNREACHABLE

1233

Удаленная система не поддерживает транспортный протокол

ERROR_PORT_UNREACHABLE

1234

He запущена ни одна служба в удаленной системе

ERROR_REQUEST_ABORTED

1235

Выполнение запроса было прервано

ERROR_CONNECTION_ABORTED

1236

Подключение к сети было разорвано

ERROR_RETRY

1237

Не удалось завершить операцию. Требуется повторить ее вызов

ERROR_CONNECTION_COUNT_LIMIT

1238

Для данной учетной записи достигнуто предельное число одновременных подключений

ERROR_LOGIN_TIME_RESTRICTION

1239

Невозможно войти в сеть для данного пользователя в непредусмотренное учетной записью время суток

ERROR_LOGIN_WKSTA_RESTRICTION

1240

С данной рабочей станции данный пользователь не может войти в сеть

ERROR_INCORRECT_ADDRESS

1241

Нельзя использовать сетевой адрес для данной операции

ERROR_ALREADY_REGI STEREO

1242

Служба уже зарегистрирована

ERROR_SERVICE_NOT_FOUND

1243

Данная служба не найдена

ERROR_NOT_AUTHENTICATED

1244

Пользователь не был зарегистрирован

ERROR_NOT_LOGGED_ON

1245

Пользователь не был подключен к сети

ERROR_CONTINUE

1246

Требуется продолжить выполняющуюся операцию

ERROR_ALREADY_INITIALIZED

1247

Операция инициализации уже была выполнена

ERROR_NO_MORE_DEVICES

1248

Больше не найдено локальных устройств

ERROR_NOT_ALL_ASSIGNED

1300

У пользователя нет некоторых прав доступа

ERROR SOME_NOT_MAPPED

1301

Нет соответствия между именами пользователей и идентификаторами защиты

ERROR_NO_QUOTAS_FOR_ACCOUNT

1302

Системные квоты для данной учетной записи не установлены

ERROR LOCAL_USER_SESSION_KEY

1303

Ключ шифрования недоступен

ERROR_NULL_LM_PAS SWORD . .

1304

Пароль Windows NT слишком сложен и не может быть преобразован в пароль LAN Manager. Вместо пароля LAN Manager была возвращена пустая строка

ERROR UNKNOWN REVISION

1305

Неизвестная версия

ERROR_REVIS ION_MI SMATCH

1306

Несовместимые версии

ERROR_INVALI D_OWNER

1307

Данный код защиты не может соответствовать владельцу объекта

ERROR INVALID PRIMARY GROUP

1308

Данный код защиты не может соответствовать основной группе объекта

ERROR_NO_IMPERSONATION_TOKEN

1309

Попытка использования представления потоком команд, который не представляет клиента

ERROR CANT_DISABLE_MANDATORY

1310

Невозможно отключить данную группу

ERROR_NO_LOGON_SERVERS

1311

В сети отсутствуют серверы, которые могут обработать запрос на вход в сеть

ERROR_NO_SUCH_LOGON_SESSION

1312

Указанный сеанс входа в сеть не существует

ERROR_NO_SUCH_PRIVILEGE

1313

Указанная привилегия не существует

ERROR_PRIVILEGE_NOT_HELD

1314

У клиента нет необходимых привилегий

ERROR_INVALI D_ACCOUNT_NAME

1315

Неправильное имя пользователя

ERROR_USER EXISTS

1316

Пользователь с данным именем уже существует

ERROR_NO_SUCH_USER

1317

Пользователь с указанным именем не существует

ERRORJ3ROUPJSXISTS

1318

Указанная группа уже существует

ERROR_NO_SUCH_GROUP

1319

Указанная группа не существует

ERROR MEMBER IN GROUP

1320

Указанный пользователь уже является членом данной группы

ERROR_MEMBER_NOT_IN_GROUP

1321

Указанный пользователь не является членом данной группы

ERROR_LAST_ADMIN

1322

Последнюю учетную запись из группы администраторов невозможно отключить или удалить

ERROR_WRONG_PAS SWORD

1323

Неверно задан старый пароль при операции смены пароля

ERROR_ILL_FORMED_PASSWORD

1324

Новый пароль содержит недопустимые символы

ERROR_PASSWORD_RESTRICTION

1325

Нарушено одно из правил обновления пароля

ERROR_LOGON_FAILURE

1326

Имя пользователя и пароль не опознаны

ERROR_ACCOUNT_RESTRICTION

1327

Имеются ограничения, связанные с учетной записью. Вход в сеть не произведен

ERROR_INVALID_LOGON_HOURS

1328

Учетная запись не допускает вход в сеть этого пользователя в данное время

ERROR_INVALID_WORKSTATION

1329

Пользователю запрещено входить в сеть с данного компьютера

ERROR_PASSWORD_EXPIRED

1330

Срок действия указанного пароля истек

ERROR_ACCOUNT_DI SABLED

1331

Учетная запись была отключена

ERROR_NONE_MAPPED

1332

Именам пользователей не сопоставлены коды защиты данных

ERROR_TOO_MANY_LUIDS_REQUESTED

1333

Одновременно запрошено слишком много локальных кодов пользователей

ERROR_LUIDS_EXHAUSTED

1334

Дополнительные локальные коды пользователей недоступны

ERROR_INVALI D_SUB_AUTHORIT Y

1335

Часть кода защиты данных содержит ошибки

ERROR_INVALI D_ACL

1336

Список управления доступом (ACL) имеет неверную структуру

ERROR_INVAL I D_S I D

1337

Код защиты данных (SID) имеет неверную структуру

ERROR_INVALID_SECURITY_DESCR

1338

Дескриптор защиты данных имеет неверную структуру

ERROR_BAD_INHERITANCE_ACL

1340

Не удается построить список управления доступом или элемент этого списка

ERROR_SERVER_DISABLED

1341

Сервер отключен

ERROR_SERVER_NOT_DI SABLED

1342

Сервер включен

ERROR_INVALID ID_AUTHORITY

1343

Недопустимое значение для защитного кода

ERROR_ALLOTTED_SPACE_EXCEEDED

1344

Недостаточно памяти для обновления сведений о защите данных

ERROR_INVALID_GROUP_ATTRIBUTES

1345

Указанные атрибуты неверны или несовместимы с атрибутами группы

ERROR_BAD_IMPERSONATION_LEVEL

1346

Неверный уровень реализации

ERROR CANT OPEN ANONYMOUS

1347

Невозможно открыть элемент защиты данных неизвестного уровня

ERROR_BAD_VALIDATION_CLASS

1348

Запрошен неверный класс сведений для проверки

ERROR BAD TOKEN TYPE

1349

Тип элемента не соответствует требуемой операции

ERROR_NO SECURITY ON_OBJECT

1350

Операция защиты данных не может быть выполнена для незащищенного объекта

ERROR CANT ACCESS DOMAIN INFO

1351

Недоступен сервер Windows NT или объекты внутри домена защищены

ERROR_INVALID SERVER_STATE

1352

Диспетчер защиты или локальный сервер не смог выполнить данную операцию

ERROR_INVALI D_DOMAIN_STATE

1353

Нельзя выполнить данную операцию при текущем состоянии домена

ERROR_INVALID DOMAIN_ROLE

1354

Данная операция определена только для основного контроллера домена

ERROR_NO_SUCH_DOMAIN

1355

Указанный домен не cyinecTsyet

ERROR_DOMAIN_EXISTS

1356

Указанный домен уже существует

ERROR_DOMAIN_LIMIT_EXCEEDED

1357

Превышен предел числа доменов, которые обслуживаются одним сервером

ERROR_INTERNAL DB CORRUPTION

1358

Не удается завершить операцию из-за сбоев ъ данных на диске

ERROR_INTERNAL_ERROR

1359

База данных системы защиты содержит внутренние противоречия

ERROR_GENERIC_NOT_MAPPED

1360

Маска доступа должна быть связана с нестандартными типами

ERROR_BAD_DESCRIPTION_FORMAT

1361

Неверный формат дескриптора защиты

ERROR NOT LOGON PROCESS

1362

Данное действие может использоваться только при входе в сеть

ERROR_LOGON_SESSION_EXISTS

1363

Запуск нового сеанса работы с сетью невозможен

ERROR_NO_SUCH_PACKAGE

1364

Неизвестный пакет программ проверки паролей

ERROR_BAD_LOGON_SESSION_STATE

1365

Невозможно выполнить операцию при текущем состоянии сеанса входа в систему

ERROR LOGON SESSION COLLISION

1366

Код сеанса уже используется

ERROR_INVALID_LOGON_TYPE

1367

Неверное задание режима входа

ERROR_CANNOT_IMPERSONATE

1368

Невозможно обеспечить реализацию

ERROR_RXACT_INVALI D_STATE

1369

Несовместимая операция для ветви реестра

ERROR_RXACT_COMMIT_FAILURE

1370

База данных защиты повреждена

ERROR_SPECIAL_ACCOUNT

1371

Операция требует особой учетной записи

ERROR_SPECIAL_GROUP

1372

Операция требует особой группы пользователей

ERROR_SPECIAL_USER

1373

Операция требует особого пользователя

ERROR_MEMBERS_PRIMARY_GROUP

1374

Невозможно удалить пользователя из основной группы

ERROR_TOKEN_ALREADY_IN_USE

1375

Элемент уже является основным

ERROR NO SUCH ALIAS

1376

Указанная локальная группа не существует

ERROR__MEMBER_NOT_IN_ALIAS

1377

Указанный пользователь не входит в локальную группу

ERROR MEMBER IN ALIAS

1378

Указанный пользователь уже является членом локальной группы

ERROR_ALIAS_EXISTS

1379

Локальная группа уже существует

ERROR LOGON NOT_GRANTED

1380

Выбранный режим входа с данного компьютера не возможен для данного пользователя

ERROR_TOO_MANY_SECRETS

1381

Достигнуто предельное значение защищенных ресурсов для одной системы

ERROR_SECRET__TOO_LONG

1382

Слишком большая длина защищенных данных

ERROR INTERNAL DB_ERROR

1383

Внутренние несоответствия в локальной базе данных защиты

ERROR TOO_MANY_CONTEXT_IDS

1384

Слишком много кодов защиты использовано в процессе входа в сеть

ERROR_LOGON_TYPE_NOT_GRANTED

1385

Выбранный режим входа с данного компьютера не возможен для данного пользователя

ERROR NT CROSS ENCRYPTION_REQUIR

1386

Для смены пароля нужен зашифрованный пароль

ERROR_NO_SUCH_MEMBER

1387

Невозможно добавление нового члена в локальную группу

ERROR INVALID MEMBER

1388

Невозможно добавление нового члена в локальную группу, т. к. он имеет неправильный тип учетной записи

ERROR TOO MANY SIDS

1389

Слишком много кодов защиты было задано

ERROR_LM_CROSS_ENCRYPTION_REQUIR

1390

Для смены пароля необходим зашифрованный пароль

ERROR_NO_INHERITANCE

1391

Список управления доступом не содержит наследуемых компонентов

ERROR_FILE_CORRUPT

1392

Файл или папка повреждены, чтение или запись невозможны

ERROR_DISK CORRUPT

1393

Структура диска повреждена

ERROR_NO_USERJ3ESSION_KEY

1394

Отсутствует раздел сеанса пользователя для заданного сеанса входа в систему

ERROR_LICENSE_QUOTA_EXCEEDED

1395

Невозможны дополнительные подключения к службе, вследствие ограничения их числа

ERROR INVALID WINDOW HANDLE

1400

Неправильный дескриптор окна

ERROR_INVALI D_MENU_HANDLE

1401

Неправильный дескриптор меню

ERROR INVALID_CURSOR_HANDLE

1402

Неправильный дескриптор указателя

ERROR_INVALID_ACCEL_HANDLE

1403

Неправильный дескриптор таблицы сочетаний клавиш

ERROR_INVALID_HOOK_HANDLE

1404

Неправильный дескриптор обработчика

ERROR_INVALI D_DWP_HANDLE

1405

Неправильный дескриптор многооконной структуры

ERROR_TLW_WITH_WSCHILD

1406

He удается создать дочернее окно

ERROR_CANNOT_FIND_WND_CLASS

1407

Не найден класс окна

ERROR_WINDOW_OF_OTHER_THREAD

1408

Окно принадлежит другому потоку

ERROR HOTKEY ALREADY REGISTERED

1409

Назначенная комбинация горячих клавиш уже зарегистрирована

ERROR_CLASS ALREADY_EXIST

1410

Класс уже существует

ERROR_CLASS DOES_NOT_EXIST

1411

Класс не существует

ERROR_CLASS_HAS WINDOWS

1412

Класс имеет открытые окна

ERROR_INVALID_INDEX

1413

Неверный индекс

ERROR_INVALID_ICON_HANDLE

1414

Неправильный дескриптор пиктограммы

ERROR PRIVATE DIALOG INDEX

1415

Ошибка при использовании ключевых слов, относящихся к окнам диалога типа private

ERROR_LISTBOX ID_NOT_FOUND

1416

Идентификатор списка не найден

ERROR_NO_WILDCARD_CHARACTERS

1417

Подстановочные знаки не обнаружены

ERROR CLIPBOARD NOT OPEN

1418

Буфер обмена не открыт

ERROR_HOTKEY_NOT_REGISTERED

1419

Данная комбинация клавиш не зарегистрирована

ERROR WINDOW NOT DIALOG

1420

Данное окно не является диалоговым окном

ERROR_CONTROL_I D__NOT_FOUND

1421

Не найден идентификатор элемента управления

ERROR_INVALI D_COMBOBOX_MESSAGE

1422

Неверное сообщение для поля со списком

ERROR_WINDOW_NOT_COMBOBOX

1423

Данное окно не является полем со списком

ERROR_INVALID_EDIT_HEIGHT

1424

Высота поля для редактирования не может превышать значения 256

ERROR DC NOT FOUND

1425

Неправильный дескриптор контекста устройства (DC)

ERROR_INVALID_HOOK_FILTER

1426

Неверный тип процедуры обработки

ERROR_INVALID_FILTER_PROC

1427

Неверная процедура обработки

ERROR_HOOK_NEEDS_HMOD

1428

Нельзя установить глобальный обработчик без дескриптора модуля

ERROR GLOBAL ONLY HOOK

1429

Данная процедура может быть только глобальной

ERROR_JOURNAL__HOOK_SET

1430

Процедура обработки журнала уже установлена

ERROR_HOOK_NOT_INSTALLED

1431

Процедура обработки не установлена

ERROR_INVALID_LB_MESSAGE

1432

Неверное сообщение для простого списка

ERROR_SETCOUNT_ON_BAD_LB

1433

Параметр LB_SETCOUNT отправлен списку неверного типа

ERROR LB WITHOUT TABSTOPS

1434

Список не входит в список табуляции

ERROR_DESTROY_OBJECT_OF_OTHER_TH

1435

Невозможно уничтожить объект, созданный в другом потоке

ERROR_CHILD_WINDOW_MENU

1436

Дочерние окна не могут содержать меню

ERROR_NO_SYSTEM_MENU

1437

Окно не имеет меню

ERROR_INVALID_MSGBOX_STYLE

1438

Неверный тип окна сообщения

ERROR_INVALID_SPI_VALUE

1439

Неверный системный параметр

ERROR_SCREEN_ALREADY_LOCKED

1440

Экран уже был заблокирован

ERROR_HWNDS_HAVE_DI FF_PARENT

1441

Дескрипторы окон многооконной структуры должны иметь общий родительский дескриптор

ERROR NOT CHILD WINDOW

1442

Окно не является дочерним

ERROR_INVALID_GW_COMMAND

1443

Неверная системная команда

ERROR_INVAL I D_THREAD_I D

1444

Неверный идентификатор потока

ERROR_NON_MDICHILD_WINDOW

1445

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

ERROR_POPUP_ALREADY_ACTIVE

1446

Всплывающее меню уже активно

ERROR_NO_SCROLLBARS

1447

Окно не имеет полос прокрутки

ERROR INVALID SCROLLBAR RANGE

1448

Неправильное значение для полосы прокрутки (оно должно быть в пределах от 0 до 7FFF)

ERROR_INVALID_SHOWWIN_COMMAND

1449

Невозможно отобразить или удалить окно данным способом

ERROR_EVENTLOG_FILE_CORRUPT

1500

Файл журнала событий поврежден

ERROR EVENTLOG CANT START

1501

Не удается найти файл журнала событий

ERROR LOG FILE FULL

1502

Файл журнала событий переполнен

ERROR_EVENTLOG_FILE_CHANGED

1503

Файл журнала событий был изменен между двумя операциями чтения

ERROR_INVALID_USER_BUFFER

1784

Данный буфер не подходит для указанной операции

ERROR UNRECOGNOZED MEDIA

1785

Не удается определить тип диска

ERROR NO TRUST LSA SECRET

1786

Рабочая станция не может участвовать в отношениях доверенности

ERROR_NO_TRUST_SAM_ACCOUNT

1787

Сервер Windows NT не содержит записи для регистрации данного компьютера через отношения доверенности

ERROR_TRUSTED_DOMAIN_FAILURE

1788

Невозможно установить отношение доверенности между основным доменом и доменом-доверителем

ERROR_TRUSTED_RELATIONSHIP_FAILURE

1789

Не удалось установить доверительные отношения между данным компьютером и основным доменом

ERROR_TRUST_FAILURE

1790

Вход в сеть не произведен

ERROR_NETLOGON_NOT_STARTED

1792

Сетевая служба входа отключена

ERROR_ACCOUNT_EXPIRED

1793

Срок действия учетной записи пользователя истек

ERROR REDIRECTOR HAS OPEN HANDLE

1794

Клиент сети занят и не может быть выгружен

ERROR_PRINTER_DRIVER_ALREADY_INST .

1795

Данный драйвер принтера уже был установлен

ERROR_UNKNOWN_PORT

1796

Попытка обращения к несуществующему порту

ERROR_UNKNOWN_PRINTER_DRIVER

1797

Неизвестный драйвер принтера

ERROR_UNKNOWN_PRINTPROCESSOR

1798

Неизвестный процессор печати

ERROR_INVALID_SEPARATOR_FILE

1799

Неверно задан файл-разделитель

ERROR_INVALID_PRIORITY

1800

Неверное задание приоритета

ERROR_INVALID_PRINTER_NAME

1801

Неверное задание имени принтера

ERROR_PRINTER_ALREADY_EXISTS

1802

Указанный принтер уже существует

ERROR_INVALID_PRINTER_COMMAND

1803

Неверная команда принтера

ERROR_INVALID_DATATYPE

1804

Неверное задание типа данных

ERROR_INVALID_ENVIRONMENT

1805

Неверное задание среды

ERROR_INVALID_PRINTER_STATE

1906

Некорректное состояние принтера

ERROR_PASSWORD_MUST_CHANGE

1907

Пользователь должен сменить пароль

ERROR_DOMAIN_CONTROLLER_NOT_FOUND

1908

Не найден контроллер домена

ERROR_ACCOUNT_LOCKED_OUT

1909

Учетная запись пользователя заблокирована

ERROR_INVALID_PIXEL_FORMAT

2000

Неверный формат точки

ERROR_BAD_DRIVER

2001

Неверный драйвер устройства

ERROR_INVALID_WINDOW_STYLE

2002

Атрибут класса окна задан неверно

ERROR_METAFILE_NOT SUPPORTED

2003

Данная операция для метафайлов не поддерживается

ERROR_TRANSFORM_NOT_SUPPORTED

2004

Данная операция преобразования не поддерживается

ERROR_CLIPPING_NONSUPPORTED

2005

Данная операция отсекания рисунка не поддерживается

ERROR_BAD_USERNAME

2202

Неверное имя пользователя

ERROR_NOT_CONNECTED

2250

Сетевое подключение отсутствует

ERROR_OPEN_FILES

2401

На подключенном устройстве имеются открытые файлы или запросы, которые ждут обработки

ERROR_ACTIVE_CONNECTIONS

2402

Имеются активные подключения

ERROR_DEVICE_IN_USE

2404

Устройство используется одним из процессов и не может быть отключено

ERROR_UNKNOWN_PRINT_MONITOR

3000

Неизвестный монитор печати

ERROR_PRINTER_DRIVER_IN_USE

3001

Драйвер принтера уже используется

ERROR_SPOOL_FILE_NOT_FOUND

3002

Не найден файл диспетчера очереди

ERROR_NO_BROWSER_SERVERS_FOUND

6118

Недоступен список серверов для данной рабочей группы






    Программирование: Языки - Технологии - Разработка