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 - список действий, которые необходимо выполнить для завершения текущего проекта.

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

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

Рис. 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).

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

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

Рис. 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
{$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 (список действий) - создает список действий, которые централизуют ответы на действия пользователя.

Рис. 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 (комбинированный список ввода времени или даты) - создает элемент, предназначенный для ввода времени или даты пользователем, путем выбора либо печати;

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

Рис. 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;
Для того чтобы убедиться, что программа работает - запустите ее на исполнение с помощью клавиши
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.

Рис. 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.

Рис. 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 |
||
Таблица 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);
- при нажатии пользователем клавиши <Пробел> или
- при нажатии пользователем клавиши
- при нажатии пользователем клавиши
- при нажатии пользователем комбинации клавиш быстрого доступа ("горячих" клавиш) для обращения к кнопке или пункту меню. Например, в свойстве caption кнопки формы записано &пуск, при этом надпись на кнопке имеет вид Пуск. В результате, когда пользователь нажимает комбинацию клавиш
- при установке приложением свойства 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 наступает, когда пользователь нажимает любую клавишу. Данное событие наступает для компонента, имеющему фокус в момент нажатия кнопки. С помощью этого события можно обрабатывать все клавиши, включая
Таблица 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 |
1В |
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 |
||
|
<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 |
6А |
VK_MULTIPLY |
||
|
<+> |
107 |
6В |
VK_ADD |
||
|
<-> |
109 |
6D |
VK_SUBTRACT |
||
|
<.> |
110 |
6Е |
VK_DECIMAL |
||
|
> |
111 |
6F |
VK_DIVIDE |
||
Данные коды не различают регистр и раскладку клавиатуры (латинская/кириллица). Параметр shift является множеством, которое может быть пустым или содержать следующие элементы:
- ssshift - при нажатой клавише
- ssAlt - при нажатой клавише
- ssctrl - при нажатой клавише
Приведем пример использования события OnKeyDown. Предположим, что нам необходимо распознать, когда пользователь нажмет комбинацию клавиш
if ((Key = ord CL1)) and (ssShift in Shift) and (ssCtrl in Shift)) then ShowMessage ('Нажата комбинация клавиш
В вышеприведенном примере мы использовали функцию ord (), которая позволяет по символу клавиши получить код клавиши (в нашем случае, код клавиши

Рис. 2.11. Окно, появляющееся при обработке события OnKeyDown
Событие onKeypress наступает при нажатии пользователем символьной клавиши. Данное событие имеет параметр Key, который содержит символ нажатой клавиши и имеет тип char. При этом различаются символы верхнего и нижнего регистров, а также раскладка клавиатуры.
Примечание
С помощью события OnKeyPress невозможно обработать нажатие функциональных клавиш и клавиш
Событие 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 - компонент привязан к правому краю родительского компонента.

Рис. 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).

Рис. 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 был найден сервер автоматизации 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.

Рис. 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).

Рис. 3.23. Окно установки элемента управления в пакет
После нажатия кнопки ОК в окне Install (Установка) Delphi откомпилирует пакет dclusrSO.dpk. В результате, содержимое пакета должно выглядеть, как представлено на рис. 3.24.

Рис. 3.24. Содержимое пакета dclusr5O.dpk после установки нового элемента управления ActiveX
Теперь перейдем к вкладке ActiveX палитры компонентов и посмотрим, какие изменения там произошли (рис. 3.25).

Рис. 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.

Рис. 3.28. Диалоговое окно мастера ActiveForm
2. В поле New ActiveX Name (Новое имя ActiveX) нам нужно ввести имя создаваемого элемента управления ActiveX. Введем имя MyForm, при этом автоматически произойдут изменения в других полях (они описаны выше для мастера ActiveX Control).
3. После нажатия кнопки ОК мастер создает все необходимые заготовки для элемента управления ActiveX (практически так же, как и в предыдущем случае). Поверхность формы MyForm становится рабочей поверхностью. Вы можете размещать на ней произвольные компоненты, описывать методы и события компонентов и формы, устанавливать их свойства.
Разместим на форме MyForm два компонента CоmВох, как показано на рис. 3.29.

Рис. 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;

Рис. 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).

Рис. 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.

Рис. 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).

Рис. 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 содержит код файла библиотеки типов.

Рис. 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 -обозревателя.

Рис. 3.34. Создание нового 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).

Рис. 3.36. Редактор библиотеки типов и MTS-объект
MTS-компоненты
MTS-компоненты обеспечивают следующие услуги низкого уровня:- управление системными ресурсами, включая процессы, потоки и поддержку одновременного подключения к базе данных большого числа пользователей;
- автоматическое управление транзакциями;
- создание, выполнение и удаление компонентов сервера;
- обеспечение безопасности вашего приложения с помощью авторизации по ролям.
Delphi предоставляет возможность использовать в качестве клиентов MTS произвольные приложения или активные формы (ActiveForms).
MTS-компоненты - это СОМ-серверы внутри процесса, которые содержатся внутри библиотек DLL. Они отличаются от других СОМ-компонентов тем, что выполняются в специальной MTS-среде. MTS-компоненты реализованы в Delphi так же, как и остальные классы.
Обычно, MTS-серверы являются небольшими объектами и используются для различных бизнес-функций. Например, MTS-компоненты могут обеспечивать бизнес-правила приложения, а также различные виды и превращения состояний приложения. Рассмотрим, например, типичное приложение, которое может использоваться в медицинских учреждениях. Как известно, различные записи баз данных хранят сведения о пациентах, их истории болезней, группы крови и многое другое. MTS-компонент позволяет в реальном времени обновлять те изменения, которые произошли. Например, был добавлен новый пациент, введены результаты анализов и т. д.
Как показано на рис. 3.33, 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 |
Недоступен список серверов для данной рабочей группы |
||
Программирование: Языки - Технологии - Разработка
- Программирование
- Технологии программирования
- Разработка программ
- Работа с данными
- Методы программирования
- IDE интерфейс
- Графический интерфейс
- Программирование интерфейсов
- Отладка программ
- Тестирование программ
- Программирование на Delphi
- Программирование в ActionScript
- Assembler
- Basic
- Pascal
- Perl
- VBA
- VRML
- XML
- Ada
- Lisp
- Python
- UML
- Форт
- Языки программирования