Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
Теперь определим интерфейс IBook, производный от IPub.
// IBook.h — интерфейс книги IBook
#ifndef _IBook_
#define _IBook_
#include "IPub.h" // IPub
DECLARE_INTERFACE_(IBook, IPub)
{
STDMETHOD(SetAuthor)(BSTR bstrAuthor) PURE;
};
#endif
Здесь просто добавляется новый метод SetAuthor, позволяющий задавать имя автора книги, которого, в общем случае, могло и не быть у публикации.
Аналогично определяется интерфейс IJournal,
// 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 равен