Выбрать главу

#include "IPub.h" // IPub

DECLARE_INTERFACE_(IBook, IPub)

{

              STDMETHOD(SetAuthor)(BSTR bstrAuthor) PURE;

};

#endif

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

Аналогично определяется интерфейс IJournal, также производный от IPub, расширяющий последний методом SetNumber — задание номера журнала.

// IJournal.h — интерфейс журнала IJournal

#ifndef _IJournal_

#define _IJournal_

#include "IPub.h" // IPub

DEC LARE_INT E RFAC E_(IJournal, IPub)

{

               STDMETHOD(SetNumber)(int nNumber) PURE;

};

#endif

В рамках модели COM каждый интерфейс должен иметь уникальный в пространстве и времени идентификатор — IID (Interface IDentifier). Идентификатор генерируется и присваивается интерфейсу при его создании и более никогда не меняется. Все реализации данного интерфейса должны использовать этот идентификатор. Пользователи обращаются к данному интерфейсу по его идентификатору. Все это позволяет не беспокоиться по поводу присваивания одного и того же имени различными разработчиками различным интерфейсам.

Существует спецификация DCE (Distributed Computing Environment — распределенная среда вычислений) от Open Software Foundation, котрая определяет UUID — Universally Unique IDentifiers (универсально уникальные идентификаторы). Эти идентификаторы формируются на основе сетевого адреса машины и точного времени, что и обеспечивает их уникальность. В СОМ эти идентификаторы получили название GUID — Globally Unique IDentifiers (глобально уникальные идентификаторы). Каждый такой идентификатор представляется 128-битным числом. В связи с тем, что не все языки программирования, поддерживающие СОМ, могут оперировать с такими большими числами, для хранения GUID используется следующая структура

typedef struct _GUID

            {    unsigned long Data1;

                 unsigned short Data2;

                 unsigned short Data3;

                 unsigned char Data4[8];

            } GUID

Эту структуру удобно задавать с помощью следующего макроса

#define DEFINE_GUID(name, \

      1, w1, w2, b1, Ь2, Ь3, Ь4, Ь5, Ь6, b7, b8) \

      EXTERN_C const GUID name \

      = { 1, w1, w2, { b1, Ь2, Ь3, Ь4, Ь5, Ь6, b7 b8 } }

Ниже представлен файл iid.h в котором определены GUID всех трех ранее определенных интерфейсов (позже в этот файл будут добавлены и GUID классов, реализующих указанные интерфейсы). Для получения GUID можно использовать утилиту guidgen.ехе из Visual Studio. Эта утилита позволяет получить определение нового GUID в виде макроса DEFINE GUID, который можно скопировать и вставить в файл определений GUID. В закоментированных строках этого файла содержатся значения GUID в виде, удобном для просмотра человеком. Имя идентификатора интерфейса стандартно формируется следующим образом — префикс IID_, за которым следует имя интерфейса. Например, IID_IPub.

// iid.h — GUID для интерфейсов

// {9A5DE 9А0-7225-11d5-9 8С7-000001223694}

DEFINE_GUID(IID_IPub,

      0x9a5de9a0, 0x7225, 0x11d5,

      0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);

// {9A5DE9A1-7225-11d5-98C7-000001223694 }

DEFINE_GUID(IID_IBook,

      0x9a5de9a1, 0x7225, 0x11d5,

      0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);

// {9A5DE 9A2-7225-11d5-98C7-000001223694}

DEFINE_GUID(IID_IJournal,

      0x9a5de9a2, 0x7225, 0xlld5,

      0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);

Реализация интерфейсов — коклассы

Теперь пора перейти к реализации наших интерфейсов. В соответствии с базовой архитектурой СОМ, интерфейсы реализуются в классах, каждый из которых может реализовать несколько интерфейсов. Далее определяются и реализуются два класса — CoBоoк и CоJournal, реализующие соответственно интерфейсы IBook и IJournal. Все названия классов в СОМ удобно начинать с префикса со, подчеркивая их принадлежность данной модели. Часто классы, определенные в СОМ, называют коклассами.

Рассмотрим вначале файл CoBоок. h

//////////////////////////////////////////////////

// СоВоок. h: заголовочный файл для класс СоВоок //

//////////////////////////////////////////////////

#ifndef _СоВоок_

#define _СоВоок_

#include "IBook.h" // определение интерфейса IBook

#include "iid.h" // GUID интерфейса IBook

class CoBook:

           public IBook

{

public:

           CoBook(); // конструктор

           virtual ~CoBook(); // деструктор

           // IUnknown

           STDMETHODIMP Querylnterface(REFIID riid, void** pIFace);

           STDMETHODIMP_(ULONG) AddRef();

            STDMETHODIMP (ULONG) Release();

           // IPub

           STDMETHODIMP SetTitle(BSTR bstrTitle);

           STDMETHODIMP SetYear(int nYear);

           STDMETHODIMP Getlnfo(BSTR* pbstrlnfo);

           // IBook

           STDMETHODIMP SetAuthor(BSTR bstrAuthor);

private:

           ULONG m_refCount; // Счетчик ссылок

           BSTR m_bstrTitle; // Название публикации

           BSTR m_bstrAuthor; // Автор публикации

           int m_nYear; // Год публикации

};

#endif

Здесь определяется класс CоBоок, порожденный от интерфейса IBook. Так как интерфейс IBook был сам порожден от интерфейса IPub, а последний — от стандартного интерфейса IUnknown, то класс CоBоок должен реализовать чисто виртуальные методы всех этих интерфейсов.

Здесь при определении методов используются следующие макросы

#define STDMETHODIMP HRESULT stdcall

#define STDMETHODIMP (type) type stdcall

теперь надо поговорить об основном стандартном интерфейсе модели COM — IUknown.

Как уже не раз говорилось, любой интерфейс СОМ должен порождаться от IUnknown или от другого интерфейса. Следовательно, все интерфейсы СОМ имеют общего предка — интерфейс IUnknown. Этот интерфейс определяется в <unknwn.h> и его GUID равен

00000000-0000-0000-С000-000000000046.

Интерфейс IUnknown определяет 3 метода, и все эти методы должны быть реализованы в каждом коклассе

• Querylnterfасе()

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

Семантика этого метода следующая. Имея указатель некоторого интерфейса pIFacel некоторого объекта и передавая в Queryinterfасе ссылку на идентификатор нового интерфейса IID_IFace2 можно получить указатель на новый интерфейс piFace2, если он реализован в данном объекте. В случае успеха возвращаемое значение S_OK, а в случае неудачи — E_NOINTERFACE