Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Роджерсон Д. - Основы COM - 2000.pdf
Скачиваний:
412
Добавлен:
13.08.2013
Размер:
2.4 Mб
Скачать

226

TangramGdiWorld

TangramGdiVisual

Список указателей на образы

ITangramGdiVisual

слабая ссылка

 

ITangramGdiWorld

m_pGdiWorld

подкомпонент

 

 

сильные ссылки

Рис. 13-7 Ссылочный цикл можно разорвать, используя подкомпоненты, поддерживающие слабые ссылки на своих родителей

События и точки подключения

До этого момента мы использовали только однонаправленную связь, когда клиент управляет компонентом. Но и компонент может выступать в качестве клиента и управлять другим компонентом. За исключением агрегирования, компоненты в этой книге никогда не имели указателя на свой клиент. В Tangram ситуация иная.

В предыдущем разделе мы видели, что у TangramGdiVisual имеется обратный указатель на TangramGdiWorld. Одно из самых распространенных применений обратного указателя — уведомление клиента о различных событиях. Как мы видели в предыдущем разделе, проще всего информировать клиент о событии при помощи слабой ссылки. Но более искусный метод состоит в том, чтобы использовать подкомпонент со слабой ссылкой на один из компонентов.

При разработке управляющих элементов ActiveX (OLE) потребовался универсальный и гибкий механизм обработки событий. В качестве решения были использованы точки подключения (connection points). Точка подключения похожа на электрический разъем. Клиент реализует интерфейс, который подключается к точке подключения. Затем компонент вызывает реализованный клиентом интерфейс. Такие интерфейсы называются

исходящими (outgoing) интерфейсами, или интерфейсами источника (source). В IDL для обозначения исходящего интерфейса используется атрибут source. (Пример можно найти в файле \TANGRAM\SOURCE\MODEL\MODEL_C.IDL.) Интерфейс называется исходящим, потому что в данном случае компонент вызывает клиент. Название «интерфейс источника» обусловлено тем, что компонент служит источником вызовов этого интерфейса.

Давайте рассмотрим очень простую схему обратного вызова. На рис. 13-8 TangramModel вызывает интерфейс ITangramModelEvent, реализованный компонентом TangramGdiVisual, однако во избежание ссылочного цикла он реализуется подкомпонентом, который перенаправляет вызовы компоненту TangramGdiVisual (Подобную реализацию мы уже встречали в разделе «Циклический подсчет ссылок».)

На рис. 13-8 TangramModel является источником вызовов интерфейса ITangramModelEvent. Подразумевается, что у ITangramModelEvent имеется функция, инициализирующая m_pEvents. Это простое решение, и оно будет работать. Но его нельзя назвать слишком гибким. Во-первых, у клиента нет стандартного способа определить, какие события поддерживаются компонентом. Во-вторых, поддерживается только один интерфейс событий. В- третьих, TangramModel может посылать события только одному клиенту. Во многих случаях необходимо информировать о произошедших событиях несколько клиентов. Эта проблема решается с помощью точек подключения.

TangramGdiWorld

m_pITangramVisual

ITangramModelEvent

ITangramModelEvent

Источник событий

TangramGdiVisual

ITangramGdiVisual

m_pGdiWorld

Получатель событий

Рис. 13-8 Простая схема обратного вызова, которая не используется в программе Tangram

Первую проблему решает интерфейс IConnectionPointContainer. Он содержит две функции: FindConnectionPoint и EnumConnectionPoints. Первая принимает IID исходящего интерфейса и возвращает указатель точки поключения для этого интерфейса. EnumConnectionPoints возвращает объект, который перечисляет все точки подключения, поддерживаемые компонентом. Это удовлетворяет второму требованию, поддержке компонентом более одного исходящего интерфейса. Перечислители, кстати, мы рассмотрим в следующем разделе.

Точка подключения — это объект, который реализует интерфейс IConnectionPoint. Каждому исходящему интерфейсу соответствует одна точка подключения. У каждой точки подключения может быть несколько

227

получателей. IConnectionPoint::EnumConnections возвращает указатель на IEnumConnections объекта, который перечисляет все подключения. Каждому получателю также может соответствовать несколько источников, но соответствующая реализация — уже задача получателя.

TangramGdiWorld

TangramGdiVisual

CEnumConnec-

 

 

m_pITangramVisual

IConnectionPointContainer

tionPoints

ITangramModelEvent

 

IEnumConnec-

 

tionPoints

 

 

 

Список ConnectionPoint

 

CTangramModelEventSink

 

CEnumCon-

 

nections

 

 

ITangramModelEvent

IConnectionPointContainer

IEnumCon-

 

 

 

 

nections

Список ConnectionPoint

Рис. 13-9 Архитектура точек подключения

На рис. 13-9 изображена архитектура точек подключения TangramModel. TangramGdiVisual использует

IConnectionPointContainer, чтобы найти IConnectionPoint, соответствующий IID_ITangramModelEvent. Компонент

TangramGdiVisual передает свой интерфейс функции IConnectionPoint::Advise. Это сообщает точке подключения о том, что TangramGdiVisual хочет получать уведомления о событиях. Для реализации точки подключения TangramModel использует несколько простых объектов СОМ. Эти объекты создаются с помощью оператора new С++, у них нет CLSID, и они не зарегистрированы в Реестре Windows. На рис. 13-9 эти объекты изображены прерывистыми линиями. Они реализуют перечислители для набора точек подключения, набор подключений и сам компонент-точку подключения. У TangramModel есть только одна точка подключения, но тем не менее реализован объект, поддерживающий интерфейс IEnumConnectionPoints. Перечислители будут рассматриваться в следующем разделе.

Представленная архитектура точек подключения (рис. 13-9) довольно сложна. Каждая точка подключения — это отдельный объект; кроме того, присутствуют два перечислителя. Но дополнительная сложность дает большую гибкость.

IEnumXXX

Наборы указателей на интерфейсы и других данных весьма важны в компонентных архитектурах. Поэтому в СОМ определен стандартный шаблон для перечисления содержимого набора. Здесь нет стандартного интерфейса, так как все версии шаблона работают с данными разных типов. Шаблон перечислителя определен как IEnumXXX, который имеет функции Reset, Next, Skip и Clone. Два примера такого интерфейса мы видели в предыдущем разделе — IEnumConnectionPoints и IEnumConnections. В гл. 6 для перечисления доступных категорий мы фактически использовали интерфейс IEnumCATEGORYINFO.

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

Использовать и реализовывать перечислители легко. Самое сложное — не забыть вызывать AddRef и Release для всех перечисляемых указателей на интерфейсы. Если Вы реализуете метод Next, следует вызвать для указателей на интерфейсы AddRef, прежде чем возвращать их клиенту. Если же Вы используете метод Next, то необходимо вызывать для возвращенных им указателей Release.

Фиксируют ли перечислители мгновенное состояние, или они «живые»? Обычно фиксируют, как, например, IEnumConnections и IEnumConnectionPoints. Объект-перечислитель, который Вы получаете в результате вызова IConnectionPoint::EnumConnections, представляет собой «мгновенный снимок» текущих подключений к данной точке. Если набор подключений изменился (из-за того, что другой клиент вызвал Advise для добавления или Unadvise для удаления подключения), Ваш мгновенный снимок обновлен не будет.

228

Основа COM — стандартные интерфейсы

Как я уже неоднократно повторял, СОМ основана на интерфейсах. Чем больше компонентов используют одни и те же интерфейсы, тем больше вероятность полиморфного использования компонентов. Многие интерфейсы уже определены СОМ, OLE, управляющими элементами ActiveX, документами ActiveX и Автоматизацией. Разработчику компонента СОМ следует изучить эти уже существующие интерфейсы. Даже если Вы решите не использовать их в своем приложении, то многое узнаете о создании с их помощью гибких компонентных архитектур.

У-у-ф!

Итак, мы подошли к концу. Вы знаете, как создавать интерфейсы СОМ на С++, реализовывать IUnknown и IClassFactory и регистрировать свои компоненты в Реестре Windows. Вам также известно, как создавать приложения из компонентов, включающих и агрегирующих другие компоненты. Вы знаете, как упростить себе жизнь с помощью классов С++ и smart-указателей. Вы также умеете описывать свои интерфейсы в файлах IDL, чтобы автоматически генерировать библиотеки маршалинга и библиотеки типа. Реализация IDispatch — это простой процесс, состоящий в использовании ITypeInfo. Наконец, Вы мастерски умеете создавать компоненты, реализующие модель разделенных потоков.

Если Вы решите написать компонент СОМ, то, учитывая Ваши знания, единственным недостающим ингредиентом будут конкретные интерфейсы СОМ. Вы знаете, как реализовать интерфейс. Теперь Вам нужно либо разработать собственный интерфейс, либо найти стандартный и реализовать его. Microsoft уже разработаны сотни интерфейсов для технологий ActiveX, DirectX и OLE. Управляющий элемент ActiveX — это просто реализация набора интерфейсов. Документ ActiveX — также набор интерфейсов с их реализациями. Управляющие элементы и документы ActiveX используют много общих стандартных интерфейсов.

Реализация интерфейсов ActiveX, DirectX и OLE — непростая задача. Однако все это, как говорится, детали реализации. Проблема заключается не в СОМ, ведь после этой книги Вы стали настоящим экспертом по СОМ. (Пришлите мне свое имя и адрес на обороте стодолларовой купюры и получите бесплатный сертификат, подтверждающий Ваши знания.) Если Вы достаточно долго поиграете с примером Tangram, то станете и танграмным мастером. Наслаждайтесь!

Соседние файлы в предмете Программирование на C++