Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3590

.pdf
Скачиваний:
0
Добавлен:
15.11.2022
Размер:
7.67 Mб
Скачать

соединении к существующему каркасу новых деталей. interface Idraw2: public Idraw interface Idraw3: public Idraw2

Здесь Idraw2 строится на основе Idraw, то есть Idraw2 является расширением Idraw. Idraw3, в свою очередь, является расширением Idraw2.

Каждый СОМ – интерфейс должен быть произведен от основного интерфейса IUnknown.

При этом используется ключевое слово interface. Оно определено в заголовочном файле

<objbase.h>:

#define interface struct

При использовании слов interface или struct по умолчанию доступ считается открытым, при использовании class – доступ к элементам структуры – закрытый.

Интерфейс IUnknown является основой любой иерархии интерфейсов, а, следовательно, и реализующих их классов. Он определяет три метода:

QueryInterface(), AddRef() и Release().

Идентификаторы интерфейсов

Интерфейсы должны быть именованы.

Каждый СОМ-интерфейс помечается универсальным уникальным идентификатором GUID. Это 128 – битное число, которое генерируется обычно на основе уникального сетевого адреса и точного времени запроса.

Виды GUID:

IID – идентификаторы интерфейса СОМ;

CLSID – уникальный идентификатор СОМкласса;

11

LIBID - уникальный идентификатор библиотеки типов;

AppID - уникальный идентификатор исполняемых файлов COM.

GUID является структурой с идущими подряд по-

лями следующих типов: long, short, short, char Data4[8].

Во многие библиотечные функции СОМ передаются GUID с помощью указателя. Для этого удобно использовать директивы препроцессора, описанные в <wtypes.h>:

#define REFGUID

const GUID*

const

#define REFIID

const IID*

const

#define REFCLSID

const CLSID* const

В <objbase.h> определены функции сравнения двух GUID, например:

BOOL IsEqualCLSID (CLSID c1, CLSID c2);

Кроме того, библиотека СОМ имеет перегруженные операторы С++:

равенства (= =) и неравенства (! = ). Примеры:

if (g1 = = g2) { ... } if (g1 ! = g2) { ... }

Создание пользовательских GUID

Каждый стандартный СОМ – интерфейс уже снабжен уникальным IID. Если разрабатывается пользовательский интерфейс, программист использует утилиту guidgen.exe для создания новых GUID. Стандартно guidgen.exe

находится в каталоге Program files\Microsoft Visual Studio\Common\Tools. Это диалоговая утилита, можно использовать также утилиту uuidgen.exe.

В программе используется макрос DEFINE_GUID, в

12

который подставляется полученное число GUID, например:

<initguid.h> //Обязательно использовать.

...

// (A533DA30-D372-11D2-B8CF-0020781238D4) DEFINE_GUID (IID_IEngine,

//префикс ___

//имя интерфейса___

0xa533da30,0xda30,0x11d2,0xb8,0xcf,0x0,0x2

0,0x78,0x12,0x38,0xd4);

Для создания GUID можно использовать библиотечную функцию СОМ

CoCreateGuid (GUID* pguid);

Ей передается указатель на GUID, и она заполняет требуемые поля.

1.1.4. Интерфейс IUnknown

Класс СОМ – это тип, определенный пользователем, как минимум содержит IUnknown. Класс СОМ также имеет название кокласс (coclass).

Объект Сом – это экземпляр кокласса. Кокласс реализует интерфейсы.

IUnknown – это стандартный интерфейс СОМ, от которого производятся все остальные интерфейсы. Во всех случаях первые три метода интерфейса СОМ – это методы, унаследованные от IUnknown. Сокращенное определение из <unknown.h>:

13

interface IUnknown

{

virtual HRESULT QueryInterface(REFID riid, void** ppv) = 0;

virtual ULONG Addref ( ) = 0; virtual ULONG Release ( ) = 0; };

IUnknown – это набор чисто виртуальных функ-

ций.

Интерфейс IUnknown обеспечивает каждому объекту СОМ две возможности:

через QueryInterface() клиент получает указатель на любой интерфейс, реализованный в объекте, из другого имеющегося указателя интерфейса;

методы AddRef() и Release() позволяют управлять временем жизни объекта, то есть определять момент, когда объект можно выгружать из памяти. Это связано с тем, что объект может использоваться разными клиентами. Для этого используется счетчик обращений к объекту, который обнуляется в конструкторе класса. В начале работы с указателем интерфейса клиент увеличивает счетчик на 1 (функция AddRef), по окончании работы клиент вызывает Release,

уменьшая счетчик на 1. Когда счетчик обнуляется, объект можно удалять.

Поскольку AddRef( ) и Release( ) это чисто виртуальные функции, то программист должен определить реализацию этих методов. В них реализуется счетчик вызовов.

14

Итак, интерфейс Iunknown отвечает за получение ссылки на интерфейс и за цикл жизни объекта.

COM-интерфейс создается на основе IUnknown: interface IDraw : public IUnknown

{

virtual void Draw() = 0;

}

При попытке вызова Draw в данном случае будет выдано сообщение об ошибке, так как отсутствует его реализация. Поэтому должна быть определена реализация метода Draw (с параметрами, если необходимо):

void Draw( . . . )

{

. . .

}

Правила вызова функций AddRef( ) и Release( )

1. Объект СОМ самостоятельно вызывает AddRef( ), когда он передает указатель интерфейса клиенту (обычно при вызове QueryInterface).

2. После получения указателя интерфейса клиент считает, что AddRef была вызвана объектом, и клиент по окончании работы с полученным указателем должен вы-

звать Release( ).

IDraw* pDraw = NULL; if (GetIDraw(&pDraw))

//Метод возвращает указатель интерфейса. { // Успешно.

pDraw -> Draw( );

//Если нет других вызовов, то объект удаляется pDraw -> Release( );

}

.

15

3. Если клиент (или сервер) делает копию указателя интерфейса, клиент должен явно вызвать AddRef() для этой копии.

pIFace2 = pIFace1; pIFace2 -> AddRef( );

4. Функции, принимающие интерфейсный указатель в качестве параметров, должны выполнять AddRef() и Release() для этого указателя:

HRESULT DrawMe(IDraw* pDrawObj)

{

 

pDrawObj -> AddRef( );

// Начало работы

pDrawObj -> Draw( );

 

// Окончание работы, освобождение объекта.

pDrawObj -> Release( );

 

}

 

Получение интерфейсных указателей с помощью метода QueryInterface( )

Метод QueryInterface( ) интерфейса IUnknown реализуется во всех коклассах и позволяет клиенту запрашивать интерфейсный указатель объекта.

Схема использования QueryInterface( ): HRESULT имя_кокласса :: QueryInterface ( REFIID riid, void** ppv)

{

// ppv – адрес выходной переменной, которая принимает

//

интерфейсный указатель, запрошенный в riid

//

проверяем по очереди все интерфейсы объекта

if (riid == IID_IUnknown)

// Это

IUnknown ?

 

16

*ppv = (IUnknown* ) this;

// Это

IUnknown

 

else if (riid = = IID_IDraw)

// Это

IDraw ?

 

*ppv = (IDraw* ) this;

// Это

Idraw

// this – поскольку ссылается сам объект

if (*ppv)

// Есть

интерфейс ?

 

{

 

((IUnknown * ) (*ppv)) -> AddRef( );

// Да

return S_OK;

}

//Интерфейс не поддерживается

*ppv = NULL

return E_NOINTERFACE;

}

Все методы интерфейса СОМ должны по возможности возвращать 32-разрядную переменную типа HRESULT. Только AddRef и Release возвращают ULONG.

Рис. 1.5 иллюстрирует структуру поля HRESULT.

Выдавшее ошибку

Информация об ошибке

средство

31

16 15

0

Рис. 1.5. Структура поля HRESULT

Старший бит определяет успешность вызова. Макросы СОМ – FAILED и SUCCEEDED проверяют старший

17

бит HRESULT и возвращают true или false. Тексты сообщений об ошибках содержатся в <winerror.h>. Сообщение об ошибке выводит функция FormatMessage().

Пример:

IDraw* pDraw = NULL; HRESULT hr;

// pUnk – ранее полученный указатель IUnknown

hr = pUnk -> QueryInterface(IID_IDraw,

 

(void**)

if (SUCCEEDED(hr))

&pDraw);

// У объекта есть IDraw ?

pDraw -> Draw();

// Есть интерфейс IDraw

18

1.2. Основы разработки программ, использующих СОМ

1.2.1. Макросы СОМ

Для определения интерфейса используются следующие макросы СОМ.

Имеются два основных набора интерфейсных макросов СОМ (один - для определения интерфейса, другой – для кокласса, реализующего интерфейс). Они определены в <objbase.h>.

Для определения интерфейса используются следующие макросы СОМ.

DECLARE_INTERFACE_ (имя_интерфейса,

имя_базового_интерфейса)

STDMETHOD (метод) (параметры)

используется при описании метода, возвращающего HRESULT, в скобках перечисляются параметры, если они есть.

STDMETHOD_ (тип, метод) (параметры)

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

PURE – заменяет текст ' = 0 ' в определении чисто виртуальной функции.

Макросам STDMETHOD и STDMETHOD_, опреде-

ляющим интерфейс, соответствуют макросы STDMETHODIMP и STDMETHODIMP_, которые реализуют интерфейс в коклассе (IMP – от слова implementation – реализация).

Пример определения интерфейса:

19

DECLARE_INTERFACE_ (IEngine, IUnknown)

{

STDMETHOD (SpeedUp) ( ) PURE; STDMETHOD (GetMaxSpeed) (int*

max_Speed) PURE;

STDMETHOD (GetCurSpeed) (int* max_Speed) PURE;

};

Пример реализации интерфейса. В описании класса, реализующего этот интерфейс, соответствующая часть выглядит следующим образом:

...

STDMETHODIMP SpeedUp ( );

STDMETHODIMP GetMaxSpeed(int* max_Speed);

STDMETHODIMP GetCurSpeed(int* CurSpeed);

...

Это был фрагмент описания класса, который обычно помещается в заголовочный файл. Реализация функции, например GetCurSpeed(), может иметь вид:

STDMETHODIMP CoCar :: GetCurSpeed ( int* CurSpeed)

{

*curSpeed = m_currSpeed; returnS_OK;

}

20

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]