- •Динамически загружаемые библиотеки
- •5.1. Динамически загружаемые библиотеки
- •5.2. Разработка библиотеки
- •5.2.1. Структура библиотеки
- •5.2.2. Экспорт подпрограмм
- •5.2.3. Соглашения о вызове подпрограмм
- •5.3. Использование библиотеки в программе
- •5.3.1. Статический импорт
- •5.3.2. Модуль импорта
- •5.3.3. Динамический импорт
- •5.3.4. Глобальные переменные и константы
- •5.3.5. Инициализация и завершение работы библиотеки
- •5.3.6. Исключительные ситуации и ошибки выполнения подпрограмм
- •5.3.7. Общий менеджер памяти
- •5.3.8. Стандартные системные переменные
- •Программирование на языке Delphi
- •6.1. Понятие интерфейса
- •6.2. Описание интерфейса
- •6.3. Расширение интерфейса
- •6.4. Глобально-уникальный идентификатор интерфейса
- •6.5. Реализация интерфейса
- •6.6. Использование интерфейса
- •6.7. Реализация нескольких интерфейсов
- •6.8. Реализация интерфейса несколькими классами
- •6.9. Связывание методов интерфейса с методами класса
- •6.10. Реализация интерфейса вложенным объектом
- •6.11. Совместимость интерфейсов
- •6.12. Совместимость класса и интерфейса
- •6.13. Получение интерфейса через другой интерфейс
- •6.14. Механизм подсчета ссылок
- •6.15. Представление интерфейса в памяти
- •6.16. Применение интерфейса для доступа к объекту dll-библиотеки
- •6.17. Итоги
- •7.1. Проект
- •7.1.1. Понятие проекта
- •7.1.2. Файлы описания форм
- •7.1.3. Файлы программных модулей
- •7.1.4. Главный файл проекта
- •7.1.5. Другие файлы проекта
- •7.2. Управление проектом
- •7.2.1. Создание, сохранение и открытие проекта
- •7.2.2. Окно управления проектом
- •7.2.3. Группы проектов
- •7.2.4. Настройка параметров проекта
- •7.2.5. Компиляция и сборка проекта
- •7.2.6. Запуск готового приложения
- •7.3. Форма
- •7.3.1. Понятие формы
- •7.3.2. Имя и заголовок формы
- •7.3.3. Стиль формы
- •7.3.4. Размеры и местоположение формы на экране
- •7.3.5. Цвет рабочей области формы
- •7.3.6. Рамка формы
- •7.3.7. Значок формы
- •7.3.8. Невидимая форма
- •7.3.9. Прозрачная форма
- •7.3.10. Полупрозрачная форма
- •7.3.11. Недоступная форма
- •7.3.12. События формы
- •7.4. Несколько форм в приложении
- •7.4.1. Добавление новой формы в проект
- •7.4.2. Добавление новой формы из Хранилища Объектов
- •7.4.3. Переключение между формами во время проектирования
- •7.4.4. Выбор главной формы приложения
- •7.4.5. Вызов формы из программы
- •7.5. Компоненты
- •7.5.1. Понятие компонента
- •7.5.2. Визуальные и невизуальные компоненты
- •7.5.3. «Оконные» и «графические» компоненты
- •7.5.4. Общие свойства визуальных компонентов
- •7.5.5. Общие события визуальных компонентов
- •7.6. Управление компонентами при проектировании
- •7.6.1. Помещение компонентов на форму и их удаление
- •7.6.2. Выделение компонентов на форме
- •7.6.3. Перемещение и изменение размеров компонента
- •7.6.4. Выравнивание компонентов на форме
- •7.6.5. Использование Буфера обмена
- •7.7. Закулисные объекты приложения
- •7.7.1. Application — главный объект, управляющий приложением
- •7.7.2. Screen — объект, управляющий экраном
- •7.7.3. Mouse — объект, представляющий мышь
- •7.7.4. Printer — объект, управляющий принтером
- •7.7.5. Clipboard — объект, управляющий Буфером обмена
- •7.8. Итоги
6.3. Расширение интерфейса
Новый интерфейс можно создать с нуля, а можно создать путем расширения уже существующего интерфейса. Во втором случае в описании интерфейса после слова interface указывается имя базового интерфейса:
type IExtendedTextReader = interface(ITextReader) procedure SkipLines(Count: Integer); end; |
Определенный таким образом интерфейс включает все методы и свойства своего предшественника и добавляет к ним свои собственные. Несмотря на синтаксическое сходство с наследованием классов, расширение интерфейсов имеет другой смысл. В классах наследуется реализация, а в интерфейсах просто расширяется набор методов и свойств.
В языке Delphi существует предопределенный интерфейс IInterface, который служит неявным базовым интерфейсом для всех остальных интерфейсов. Это означает, что объявление
type ITextReader = interface ... end; |
эквивалентно следующему:
type ITextReader = interface(IInterface) ... end; |
Мы рекомендуем использовать вторую, более полную форму записи.
Описание интерфейса IInterface находится в стандартном модуле System:
type IInterface = interface ['{00000000-0000-0000-C000-000000000046}'] function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; |
Непонятная последовательность нулей и других цифр в квадратных скобках — это так называемый глобально-уникальный идентификатор интерфейса. Мы к нему еще вернемся, а сейчас рассмотрим методы.
Методы интерфейса IInterface явно или неявно попадают во все интерфейсы и имеют особое назначение. Метод QueryInterface нужен для того, чтобы, имея некоторый интерфейс, запросить у объекта другой интерфейс. Этот метод автоматически вызывается при преобразовании одних интерфейсов в другие. Метод _AddRef автоматически вызывается при присваивании значения интерфейсной переменной. Метод _Release автоматически вызывается при уничтожении интерфейсной переменной. Последние два метода позволяют организовать подсчет ссылок на объект и автоматическое уничтожение объекта, когда количество ссылок на него становится равным нулю. Вызовы всех трех методов генерируются компилятором автоматически, и вызывать их явно нет необходимости, однако программист должен позаботиться об их реализации.
6.4. Глобально-уникальный идентификатор интерфейса
Интерфейс является особым типом данных: он может быть реализован в одной программе, а использоваться из другой. Для этого нужно обеспечить идентификацию интерфейса при межпрограммном взаимодействии. Понятно, что программный идентификатор интерфейса для этого не подходит — разные программы пишутся разными людьми, а разные люди подчас дают одинаковые имена своим творениям. Поэтому каждому интерфейсу выдается своеобразный «паспорт» — глобально-уникальный идентификатор (Globally Unique Identifier — GUID).
Глобально-уникальный идентификатор — это 16-ти байтовое число, представленное в виде заключенной в фигурные скобки последовательности шестнадцатеричных цифр:
{DC601962-28E5-4BF7-9583-0CE22B605045} |
В среде Delphi глобально-уникальный идентификатор описывается типом данных TGUID:
type PGUID = ^TGUID; TGUID = packed record D1: Longword; D2: Word; D3: Word; D4: array[0..7] of Byte; end; |
Константы с типом TGUID разрешено инициализировать строковым представлением глобально-уникального идентификатора. Компилятор сам преобразует строку в запись с типом TGUID. Пример:
const InterfaceID: TGUID = '{DC601962-28E5-4BF7-9583-0CE22B605045}'; |
Если глобально-уникальный идентификатор назначается интерфейсу, то он записывается после ключевого слова interface и заключается в квадратные скобки, например:
type IInterface = interface ['{00000000-0000-0000-C000-000000000046}'] ... end; |
В будущем нашему интерфейсу ITextReader понадобится глобально-уникальный идентификатор. Но как его выбрать так, чтобы он оказался уникальным? Очень просто — нажмите в редакторе кода комбинацию клавиш Ctrl+Shift+G.
type ITextReader = interface ['{DC601962-28E5-4BF7-9583-0CE22B605045}'] // Результат нажатия Ctrl+Shift+G ... end; |
Генерация глобально-уникальных идентификаторов осуществляется системой Windows по специальному алгоритму, в котором задействуется адрес сетевого адаптера, текущее время и генератор случайных чисел. Можете смело полагаться на уникальность всех получаемых идентификаторов.
Наличие глобально-уникального идентификатора в описании интерфейса не является обязательным, однако использование интерфейса без такого идентификатора ограничено, например, запрещено использовать оператор as для преобразования одних интерфейсов в другие.
Если у интерфейса есть глобально-уникальный идентификатор, то программный идентификатор интерфейса можно использовать там, где ожидается тип данных TGUID, например:
const IID_ITextReader: TGUID = '{DC601962-28E5-4BF7-9583-0CE22B605045}';
function TestInterface(const IID: TGUID): Boolean;
begin ... TestInterface(ITextReader); // эквивалентно TestInterface(IID_ITextReader); ... end; |