Модель программирования Component Object Model. Разработка COM-сервера, страница 16

/* interface */ ISay;

// Smart pointer typedef declarations

_COM_SMARTPTR_TYPEDEF(ISay, __uuidof(ISay));

// Type library items

struct __declspec(uuid("9b865820-2ffa-11d5-98b4-00e0293f01b2"))

CoSay;

// [ default ] interface ISay

struct __declspec(uuid("170368d0-85be-43af-ae71-053f506657a2"))

ISay : IUnknown

{

// Wrapper methods for error-handling

HRESULT Say ( );

HRESULT SetWord (_bstr_t word);

// Raw methods provided by interface

virtual HRESULT __stdcall raw_Say ( ) = 0;

virtual HRESULT __stdcall raw_SetWord (

/*[in]*/ BSTR word ) = 0;

};

// Named GUID constants initializations

extern "C" const GUID __declspec(selectany) LIBID_MyCom =

{0x0934da90,0x608d,0x4107,{0x9e,0xcc,0xc7,0xe8,0x28,0xad,0x09,0x28}};

extern "C" const GUID __declspec(selectany) CLSID_CoSay =

{0x9b865820,0x2ffa,0x11d5,{0x98,0xb4,0x00,0xe0,0x29,0x3f,0x01,0xb2}};

extern "C" const GUID __declspec(selectany) IID_ISay =

{0x170368d0,0x85be,0x43af,{0xae,0x71,0x05,0x3f,0x50,0x66,0x57,0xa2}};

// Wrapper method implementations

#include "c:\myprojects\saytlibclient\debug\MyComTLib.tli"

#pragma pack(pop)

Код TLH-файла (Type Library Header) имеет шаблонную структуру. Он представляет собой код С++, являющийся эквивалентом двоичного файла MyComTLib.tlb — библиотеки типов. Вы помните, что саму библиотеку сгенерировал компилятор при создании COM-сервера. Файлы tli и tlh описывают функциональность COM-сервера и используются в процессе отладки клиента (они заменяют сервер). Для нас наибольший интерес представляет код, который следует после упреждающих объявлений регистрируемых объектов. Это — объявление специального указателя (smart-pointer):

_COM_SMARTPTR_TYPEDEF(ISay, __uuidof(ISay));

Для того, чтобы добавить секретности здесь опять использован макрос, который при расширении превратится в:

typedef _com_ptr_t<_com_IIID<ISay, __uuidof(ISay)> > ISayPtr;

Как вы, вероятно, догадались, лексемы com_ptr_t и _com_IIID представляют собой шаблоны классов, первый из которых создает новый класс С++, инкапсулирующий функциональность зарегистрированного интерфейса ISay, а второй — класс указателя на этот класс. Операция typedef удостоверяет появление нового типа данных ISayPtr. Отныне объекты типа ISayPtr являются указателями на класс, скроенный по сложному шаблону. Рассмотрим содержимое второго файла заголовков MyComTLib.tli.

#pragma once

inline HRESULT ISay::Say ( ) // interface ISay wrapper method implementations

{

HRESULT _hr = raw_Say();

if (FAILED(_hr))

_com_issue_errorex(_hr, this,__uuidof(this));

return _hr;

}

inline HRESULT ISay::SetWord ( _bstr_t word )

{

HRESULT _hr = raw_SetWord(word);

if (FAILED(_hr))

_com_issue_errorex(_hr, this,__uuidof(this));

return _hr;

}

Здесь, как вы видите, расположены тела wrapper-методов, заменяющих методы нашего интерфейса. Характерно то, что методы интерфейса ISay (Say и SetWord) заменяются на эквивалентные виртуальные методы шаблонного класса (raw_Say и raw_SetWord). Сейчас уместно вновь проанализировать код клиентского приложения и постараться увидеть его в новом свете, зная о существовании нового типа ISayPtr. Теперь становится понятной строка объявления:

ISayPtr pSay (CLSID_CoSay);

Здесь создается объект pSay класса, эквивалентного типу ISayPtr, и вызывается его конструктор. C помощью умного указателя pSay вместо прямых вызовов методов Say и SetWord, теперь будут происходить их косвенные вызовы из функций-оберток (raw_Say и raw_SetWord), но при этом исчезает необходимость вызывать методы CreateInstance и Release.

Как работают Smart-pointers

Smart-pointer — это класс, в котором переопределена операция выбора operator->. Этот класс содержит указатель на объект другого класса. Вызов operator-> передается этому указателю. Рассмотрим пример.

class C { public: virtual void Do() { cout<<"\nDoing"; } };

class Smart //==== Класс умного указателя

{

C* ptr;  // Указатель на другой класс

public:

Smart (C* p) { ptr = p; }   // Запоминаем указатель на другой класс