Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Интерфейс IUnknown

Базовым интерфейсом в модели СОМ является интерфейс IUnknown. Любой другой интерфейс наследуется от IUnknown и должен реализовать объявленные в нем методы. Интерфейс IUnknown объявлен в модуле System.pas следующим образом:

type IInterface = interface

[‘{00000000-0000-0000-C000-000000000046}’]

function QueryInterface(const IID: TGUID; outObj): HResult; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

end;

IUnknown = IInterface;

 

В связи с поддержкой мультиплатформенной разработки (Kylix) в последних версиях Delphi базовым интерфейсом является интерфейс IInterface, описание которого полностью совпадает с описанием интерфейса IUnknown. Поскольку рассматриваются технологии СОМ, в дальнейшем в качестве базового интерфейса будем считать интерфейс IUnknown.

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

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

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

Также следует принять во внимание, что один и тот же интерфейс может быть затребован одновременно несколькими клиентами. Например, пусть два клиентских приложения одновременно работают с Microsoft Word. Если производить освобождение ресурсов интерфейса тем же способом, что и освобождение ресурсов класса, то первое клиентское приложение просто закроет Word. Второе, в общем случае, не будет уведомлено об этом, поэтому оно сохранит ссылку на несуществующий объект. Нетрудно догадаться, что произойдет в этом случае при попытке вызвать методы этого объекта.

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

Таким образом, когда клиент требует ссылку на интерфейс, клиентом в адресном пространстве сервера создается об ъект, резервируются ресурсы и вызывается метод _AddRef. Если другой клиент затребует тот же самый интерфейс, то, чаще всего, увеличивается счетчик ссылок на уже имеющийся объект, и указатель передается второму клиенту (допустима ситуация, когда создается вторая копия объекта в памяти, но счетчик ссылок в обеих копиях остается равным единице). Соответственно, когда клиенту уже не нужен интерфейс, он вызывает метод _Release. Этот метод уменьшает счетчик ссылок на единицу. Одновременно проверяется, равен ли счетчик ссылок нулю, и, если равен, вызывается деструктор объекта.

Вызов деструктора происходит из сервера — поэтому ресурсы освобождаются корректно.

Интерфейс IUnknown содержит также метод QueryInterface, который используется для получения клиентом ссылок на интерфейс. Клиент вызывает метод QueryInterface интерфейса IUnknown COM-сервера и указывает идентификатор IID (Interface Identifier — идентификатор интерфейса) того интерфейса, ссылку на который он хочет получить. IID — это тот же самый тип данных, что и GUID, и применяется он для идентификации интерфейсов. Ссылка на интерфейс IUnknown сервера становится доступной при помощи фабрики классов ICIassFactory. Метод QueryInterface обязан проверять все идентификаторы IID интерфейсов, реализованные в данном классе многокомпонентного объекта (CoClass, СОМ-класс). Если будет найден совпадающий идентификатор IID, то метод QueryInterface обязан сделать следующее.

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

2. Вызвать метод _AddRef для затребованного интерфейса и тем самым увеличить счетчик ссылок на 1. Иногда для группы интерфейсов реализуется общий счетчик ссылок.

3. Поместить указатель на созданный (или имеющийся) объект, в котором реализован интерфейс, в нетипизированную переменную Р.

4. Возвратить результат S_OK.

Если же интерфейс с данным идентификатором IID не поддерживается, то в нетипизированную переменную Р возвращается nil, и результат вызова метода должен быть равен E_NOINTERFACE.

 

Типичный пример реализации метода QueryInterface приведен ниже:

Function TMyClassFactory.QueryInterface(const iid:TIID; var Р): HResult;

begin

if IsEqualIID(iid, IID_IClassFactory) or

IsEqualIID(iid,IID_IUnknown) then begin

Pointer(P):= Self;

_AddRef;

Result:= S_OK;

end else begin

Pointer(P):= nil;

Result:= E_NOINTERFACE;

end;

end;

 

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

Кроме этого, поддержка интерфейсов реализована в базовом классе TObject. Он имеет следующий метод:

function TObject.GetInterface(const IID: TGUID; out Obj): Boolean;

Если класс реализует запрошенный интерфейс, то эта функция:

· возвращает ссылку на этот интерфейс в параметре Obj;

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

· возвращает True.

В противном случае функция возвращает False.

Таким образом, имеется возможность запросить у любого класса Delphi реализуемый им интерфейс.

<== предыдущая лекция | следующая лекция ==>
Объявление интерфейсов | Реализация интерфейсов в Delphi
Поделиться с друзьями:


Дата добавления: 2014-01-05; Просмотров: 468; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.029 сек.