logo
Программирование в среде Delphy / Программирование в среде Delphi

26.1. Интерфейс

Интерфейс – это определение методов и свойств, которые могут быть реализованы классом. Все методы в интерфейса рассматриваются как виртуальные и абстрактные по определению. Первоначально определенный интерфейс нельзя изменять в дальнейшем. Любое внесение изменений в интерфейс дает уже другой интерфейс. Каждый интерфейс определяется специальным, глобальным идентификатором – GUID (Global Unique IDentifier) – шестнадцатибайтовым числом. Для интерфейсов такой идентификатор носит название IID (Interface Identifier). В Delphi при нажатии клавиш Ctrl+Shift+G новый GUID формируется автоматически по специальным правилам, исключающим повторение значения GUID–а. Интерфейсы могут наследоваться. Прародителем всех интерфейсов является IUnKnown – неизвестный. В Windows он определен следующим образом:

Type IUnknown = interface

['{00000000-0000-0000-C000-000000000046}'] // GUID интерфейса

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

end;

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

а) возвращает ссылку на него в параметре Obj;

б) вызывает метод _AddRef полученного интерфейса;

в) возвращает 0.

В противном случае функция возвращает код ошибки – ENOINTERFACE.

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

В модуле System.pas объявлен класс TInterfaceObject, реализующий интерфейс IUnknown и его методы. Кроме того, поддержка интерфейсов реализована в базовом классе TObject.

Реализацией интерфейса в Delphi всегда выступает класс. Для этого в объявлении класса необходимо указать, какие интерфейсы он реализует. Например:

Type TMyClass=Class(TComponent, IMy1Interface, IMy2Interface)

// Реализация методов

end;

В данном примере класс TMyClass является наследником класса TComponent и он реализует два интерфейса: IMy1Interface и IMy2Interface. Таким образом, класс поддерживает множественное наследование интерфейсов и единичное наследование классов. Когда класс реализует интерфейс, он ответственен за реализацию всех методов не только этого интерфейса, но и методов всех предков интерфейса.

Имя интерфейса принято начинать с буквы «I». Зарегистрированные в системе IID и имена интерфейсов хранятся в ветке реестра – HKEY_CLASSES_ROOT\Interface.

26.2. COM–сервер

COM–сервер – это специальным образом оформленное и зарегистри-рованное приложение, которое позволяет клиентам запрашивать создание реализованных в сервере объектов. Сервер может быть выполнен в виде либо динамической библиотеки (*.dll), либо исполняемого файла (*.exe).

Сервер в виде dll всегда выполняется в адресном пространстве активизировавшего его приложения. При этом производится быстрый вызов методов, но он менее надежен, так как не защищен от ошибок вызывающего его приложения.

Сервер в виде exe файла представляет собой обычный исполняемый файл, в котором реализована возможность создания COM–объектов по запросу клиента. Примером такого сервера может служить пакет Microsoft Office, приложения которого являются COM–серверами.

COM реализует механизм автоматического поиска серверов по запросу клиента. Каждый COM–объект имеет уникальный идентификатор Class Identifier (CLSID), построенный так же, как и GUID. Все COM–объекты регистрируются ОС Windows в ветке реестра HKEY_CLASSES_ROOT\CLSID. Для каждого сервера здесь прописывается информация нужная для нахождения и загрузки его модуля, соответствующее «дружественное» имя или Programmatic Identifier (PROGID).

Серверы в виде исполняемых файлов автоматически регистрируются в системе при первом запуске программы на компьютере. Для регистрации DLL–серверов используется программа Regsvr32 из системы Windows или процедура RegisterCOMServer из поставки Delphi.

Windows – многозадачная и многопоточная среда с вытесняющейся многозадачностью. Для COM это означает, что клиент и сервер могут оказаться в разных процессах или потоках и нужно обеспечить обращение к серверу множества клиентов в непредсказуемое время. Технология COM решает эту проблему с помощью «комнат» (Apartments), в которых выполняются COM–клиенты и COM–серверы. Комнаты бывают однопоточные (Single Threaded Apartment – STA) и многопоточные (Multiple Threaded Apartment – MTA).

При STA COM создает невидимое окно и при вызове любого метода COM–сервера Windows посылает сообщение этому окну с помощью функции PostMessage. Таким образом, образуется очередь сообщений для данного сервера. Это позволяет не заботиться о синхронизации методов и при этом нет опасности одновременного доступа нескольких методов к одному полю.

Для MTA в одной «комнате» может быть сколько угодно потоков и объектов, но при этом надо заботиться о синхронизации вызовов методов. Разработка таких серверов сложна, но они обладают более высоким быстродействием.

Для EXE–серверов клиент и сервер находятся в разных адресных пространствах. Для их взаимодействия в «комнате» клиента создается представитель сервера – Proxy, который представляет собой объект, экспортирующий запрошенный интерфейс. Одновременно в комнате сервера создается объект–заглушка (Stub), принимающий вызовы от proxy и транслирующий их в вызовы сервера. Процесс обмена между клиентом и сервером называется Marshalling. При этом клиент и сервер могут находиться на разных компьютерах.

Процесс запуска COM сервера по запросу клиента происходит следующим образом:

1) в реестре по запрошенному CLSID ведется поиск записи регистрации сервера;

2) из этой записи получаем имя исполняемого модуля сервера;

3) если это – исполняемый файл, то он запускается на выполнение. Любое приложение, реализующее COM–сервер, при старте регистрирует в системе интерфейс «фабрики объектов». После запуска и регистрации COM получает ссылку на «фабрику объектов»;

4) если это – DLL, то она загружается в адресное пространство вызвавшего процесса и вызывается ее функция DLLGetClassObject, возвращающая ссылку на реализованную в DLL «фабрику объектов»;

5) «фабрика объектов» – это COM–сервер, реализующий интерфейс IClassFactory. Ключевым методом этого интерфейса является CreateInstance, который и создает экземпляр требуемого объекта;

6) COM вызывает метод CreateInstance и передает полученный интерфейс клиенту.

Для создания COM–сервера в Delphi нужно в меню пройти путь:

File → New → ActiveX

На страничке ActiveX находятся значки для управления процессом создания различных видов COM–объектов. Можно создать COM–сервер в виде EXE–файла путем добавления COM–объекта к текущему проекту. Для создания COM–сервера в виде DLL–библиотеки можно выбрать значок ActiveX Library. Обычно создание COM–сервера сопровождается созданием соответствующей библиотеки типов, предназначенной для документирования информации об объектах, интерфейсах, функциях и т.д. Для ее создания на страничке ActiveX есть значок Type Library. Традиционно для создания библиотек типов предназначен язык описания интерфейсов IDL (Interface Description Language). В Delphi в качестве основного варианта используются синтаксис и операторы языка Object Pascal. Для работы с библиотекой типов используется специальный редактор, который можно найти, пройдя путь Windows → *_TLB.pas Editor. Библиотека создается как обычный текстовый файл с расширением Pas и окончанием _TLB в названии. Редактор типов может экспортировать библиотеку из текстового формата в формат IDL (двоичный), что обеспечивает использование созданных объектов в любых других приложениях Windows. Библиотека типов дает возможность при создании клиента контролировать правильность обращения к методам сервера еще на этапе проектирования. С другой стороны, можно импортировать в проект любую библиотеку типов, зарегистрированную в системе. Для этого следует пройти путь Project → Import Type Library.