Добавить в цитаты Настройки чтения

Страница 176 из 372

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

Известно, что в разных языках программирования строки организованы различным образом. В СОМ выбрано представление строки, позволяющее работать с ним в программах, написанных на различных языках. Переменная типа BSTR (BASIC String) есть строка, представленная в формате Unicode (2 байта на один символ), с завершающим нулем и с префиксом (4 байта), хранящим длину строки (что позволяет сохранять внутри строки нулевые символы). Имеется ряд функций, облегчающих работу с такими строками (которые для C++ будут продемонстрированы в последующих примерах).

И, наконец, напомним, что в реализации данного примера средствами ООП в описании класса CPublication имелся метод Display, который обеспечивал вывод информации о публикации на терминал.

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

Теперь определим интерфейс 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, также производный от 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оок должен реализовать чисто виртуальные методы всех этих интерфейсов.