Лекция 1 Введение в современные технологии программирования

Вид материалаЛекция

Содержание


IFormattedNumber = interface
Автоматическое управление памятью и подсчет ссылок
Объявление интерфейсов
Интерфейс lUnknown
Подобный материал:
1   2   3   4

IFormattedNumber = interface


['{2DE825C1-EADF-11D2-B39F-0040F67455FE}']

function FormattedString: string;

end;

то следующие два объявления приведут к одному и тому же результату:

MyGuid := ['{2DE825C1-EADP-11D2-B39F-0040F67455FE}']

MyGuid *TGUID =[Dl: $2DE825C1; D2: $EADF; D3: $11D2; D4: ($B3, $9F, $00, $40, $F6, $74,$55,$FE}]

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

Автоматическое управление памятью и подсчет ссылок

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

Объявление интерфейсов

Для поддержки интерфейсов Delphi расширяет синтаксис языка Pascal допол­нительными ключевыми словами. Объявление интерфейса в Delphi реализуется с помощью ключевого слова interface:

type

IMyInterface = interface

[‘{412AFFOO-5C21-11D4-84DD-C8393F763A13}']

procedure DoSomething(var I: Integer); stdcall;

function DoSomethingAnother(S: String): Boolean;

end;

IMyInterface2 = interface (IMyInterface)

[‘{412AFF01-5C21-11D4-84DD-C8393F763A13}’]

procedure DoAdditional(var I: Integer): stdcall;

end:

Для генерации нового значения GUID в среде разработки Delphi использует­ся сочетание клавиш Ctrl+Shift+G.

Интерфейс lUnknown

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

type

lUnknown - interface

['{00000000-0000-0000-С000-000000000046}‘]

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

function _AddRef: Integer: stdcall:

function _Re1ease: Integer: stdcall;

end;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

begin

if IsEqualIID(iid, IID_ICIassFactory) 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:

В принципе, в конкретной реализации методы интерфейса IUnknown могут содержать и какую-либо другую функциональность, отличающуюся от стандартной, однако делать этого не рекомендуется, поскольку в этом случае интерфейс будет несовместим с моделью СОМ.

Контрольные вопросы
  1. Что такое компонент? Чем он отличается от класса?
  2. Что такое интерфейс?
  3. Какие формы представления интерфейса вы знаете?
  4. Чем полезен интерфейс?
  5. Каково назначение COM? Какие преимущества дает использование COM?
  6. Чем COM-объект отличается от обычного объекта?
  7. Что должен иметь клиент для использования методов COM-объекта?
  8. Как идентифицируется COM-интерфейс?
  9. Как описывается COM-интерфейс?
  10. Как реализуется COM-интерфейс?
  11. Чего нельзя делать с COM-интерфейсом? Обоснуйте ответ.
  12. Объясните назначение и применение метода QueryInterface?
  13. Объясните назначение и применение методов AddRef и Release?
  14. Что такое сервер COM-объекта и какие типы серверов вы знаете?
  15. В чем назначение библиотеки COM?
  16. Как создается одиночный COM-объект?
  17. Как создается несколько COM-объектов одного и того же класса?
  18. Как обеспечить использование нового COM-класса старыми клиентами?
  19. В чем состоит особенности повторного использования COM-объекта?
  20. Какие требования предъявляет агрегация к внутреннему COM-объекту?
  21. Что такое маршалинг и демаршалинг?
  22. Поясните назначение посредников и заглушки.
  23. Зачем нужна библиотека типа и как она описывается?



Литература
  1. Вельбицкий И.В. Технология программирования. –К.: Техніка, 1984. – 279 с., ил. – (Б-ка инженера). – Библиогр.: с. 274 – 277.
  2. Давид Чеппел. Технология ActiveX и OLE/Пер. с англ.. – М: Издательский отдел „Русская Редакция” ТОО „Channel Trading Ltd.@, 1997. – 320 с.: ил.
  3. Орлов С.А. Технология разработки программного обеспечения: Учебник. – СПб.: Питер, 2002. – 464 с.: ил.
  4. Шеферд Джордж. Программирование на Microsoft Visual C++.NET. Мастер-класс./Пер. с англ. – 2-е изд. – М.: Издательско-торговый дом «Русская Редакция»; СПб.: Питерб 2005. – 928 стр.: ил.