Изменить стиль страницы

class SafeRect : public IRect {

LONG m_cRef;

// СОМ reference count

// счетчик ссылок СОМ IUnknown *m_pUnkFTM;

// cache for FTM lazy aggregate

// кэш для отложенного агрегирования FTM

DWORD m_dwTopLeft;

// GIT cookie for top/left

// закладка GIT для верхнего/левого

DWORD m_dwBottomRight;

// GIT cookie for bottom/right

// закладка GIT для нижнего/правого

Разработчик по-прежнему создает два экземпляра Point, но вместо хранения исходных указателей регистрирует интерфейсные указатели с помощью глобальной таблицы GIT:

SafeRect::SafeRect(void) : m_cRef(0), m_pUnkFTM(0) {

// assume ptr to GIT is initialized elsewhere

// допустим, что указатель на GIT инициализирован

// где-нибудь в другом месте

extern IGIobalInterfaceTable *g_pGIT;

assert(g_pGIT != 0);

IPoint *pPoint = 0;

// create instance of class Point

// создаем экземпляр класса Point HRESULT

hr = CoCreateInstance(CLSID_Point, 0, CLSCTX_INPROC, IID_Ipoint, (void**)&pPoint);

assert (SUCCEEDED (hr));

// register interface pointer in GIT

// регистрируем интерфейсный указатель в GIT

hr = g_pGIT->RegisterInterfaceInGlobal(pPoint, IID_Ipoint, &m_dwTopLeft);

assert(SUCCEEDED(hr));

pPoint->Release();

// reference is now held in GIT

// ссылка теперь содержится в GIT

// create instance of class Point

// создаем экземпляр класса Point

hr = CoCreateInstance(CLSID_Point, 0, CLSCTX_INPROC, IID_Ipoint, (void**)&pPoint);

assert(SUCCEEDED(hr));

// register interface pointer in GIT

// регистрируем интерфейсный указатель в GIT

hr = g_pGIT->RegisterInterfaceInGlobal(pPoint, IID_Ipoint, &m_dwBottomRight);

assert(SUCCEEDED(hr)); pPoint->Release();

// reference is now held in GIT

// ссылка теперь содержится в GIT

}

Отметим, что все то время, пока интерфейсный указатель зарегистрирован в GIT, пользователь интерфейсного указателя не должен хранить никаких дополнительных ссылок.

Поскольку класс был преобразован для использования GIT вместо исходных интерфейсных указателей, он должен демаршалировать новый заместитель в каждом вызове метода, которому требуется доступ к зарегистрированным интерфейсам:

STDMETHODIMP SafeRect::get_Area(long *pn) {

extern IGlobalInterfaceTable *g_pGIT; assert(g_pGIT != 0);

// unmarshal the two interface pointers from the GIT

// демаршалируем дВа интерфейсных указателя из GIT

IPoint *ptl = 0, *pbr = 0;

HRESULT hr = g_pGIT->GetInterfaceFromGlobal(m_dwPtTopLeft, IID_Ipoint, (void**)&ptl);

assert (SUCCEEDED(hr));

hr = g_pGIT->GetInterfaceFromGlobal(m_dwPtBottomRight, IID_Ipoint, (void**)&pbr);

// use temp ptrs to implement method

// дпя реализации метода используем временные указатели

long top, left, bottom, right;

hr = ptl->GetCoords(&left, &top);

assert (SUCCEEDED(hr));

hr = pbr->GetCoords(&right, &bottom);

assert (SUCCEEDED (hr));

*pn = (right – left) * (bottom – top);

// release temp ptrs. // освобождаем временные указатели

ptl->Release();

pbr->Release();

return S_OK;

}

Поскольку реализация SafeRect использует FTM, то нецелесообразно пытаться сохранить немаршалированные интерфейсные указатели между вызовами метода, так как неизвестно, произойдет ли следующий вызов метода в том же самом апартаменте.

Все зарегистрированные интерфейсные указатели будут храниться в таблице GIT до тех пор, пока они не будут явно удалены нз GIT. Это означает, что класс SafeRect должен явно аннулировать элементы GIT для двух своих элементов данных:

SafeRect::~SafeRect(void) {

extern IGlobalInterfaceTable *g_pGIT;

assert(g_pGIT != 0);

HRESULT hr = g_pGIT->RevokeInterfaceFromGlobal(m_dwTopLeft);

assert(SUCCEEDED(hr));

hr = g_pGIT->RevokeInterfaceFromGlobal(m_dwBottomRight);

assert(SUCCEEDED(hr));

}

Удаление интерфейсного указателя из GIT освобождает все хранящиеся ссылки на объект.

Отметим, что совместное использование GIT и FTM влечет за собой очень много обращений к GIT, которые будут сделаны для создания временных интерфейсных указателей, необходимых для использования в каждом отдельном методе. Хотя GIT оптимизирована именно для поддержки такой схемы использования, код остается однообразным. Следующий простой класс C++ скрывает использование «закладки» GIT за удобным интерфейсом, обеспечивающим безопасность типа:

template class GlobalInterfacePointer {

DWORD m_dwCookie;

// the GIT cookie

// «закладка» GIT

// prevent misuse

// предотвращаем неправильное использование

GlobalInterfacePointer(const GlobalInterfacePointer&);

void operator =(const GlobalInterfacePointer&);

public:

// start as invalid cookie

// начинаем как неправильная «закладка»

GlobalInterfacePointer(void) : m_dwCookie(0) { }

// start with auto-globalized local pointer

// начинаем с автоматически глобализованным локальным указателем

GlobalInterfacePointer(Itf *pItf, HRESULT& hr) : m_dwCookie(0)

{ hr = Globalize(pItf); }

// auto-unglobalize

// осуществляем автоматическую деглобапизацию

~GlobalInterfacePointer(void) { if(m_dwСооkiе) Unglobalize() ; }

// register an interface pointer in GIT

// регистрируем интерфейсный указатель в GIT

HRESULT Globalize(Itf *pItf) { assert (g_pGIT != 0 && m_dwCookie == 0);

return g_pGIT->RegisterInterfaceInGlobal(pItf, * piid, &m_dwCookie);

}

// revoke an interface pointer in GIT

// аннулируем интерфейсный указатель в GIT

HRESULT Unglobalize(void) {

assert(g_pGIT != 0 && m_dwCookie != 0);

HRESULT hr = g_pGIT->RevokeInterfaceFromGlobal(m_dwCookie);

m_dwCookie = 0;

return hr;

}

// get а local interface pointer from GIT

// получаем локальный интерфейсный указатель из GIT

HRESULT Localize(Itf **ppItf) const {

assert(g_pGIT != 0 && m_dwCookie != 0);

return g_pGIT->GetInteгfaceFromGlobal(m_dwCookie, *piid, (void**)ppItf);

}

// convenience methods

// методы для удобства

bool IsOK(void) const { return m_dwCookie != 0; }

DWORD GetCookie(void) const { return m_dwCookie; }

};

#define GIP(Itf) GlobalInterfacePointer

Имея данное определение класса и макрос, класс SafeRect теперь вместо исходных DWORD сохраняет GlobalInterfacePointers:

class SafeRect : public IRect {

LONG m_cRef:

// СОM reference count

// счетчик ссылок СОМ

IUnknown *m_pUnkFTM;

// cache for FTM lazy aggregate

// кэш дпя отложенного агрегирования FTM

GIP(IPoint) m_gipTopLeft;

// GIT cookie – top/left

// «закладка» GIT для верхнего/левого элемента

GIP(IPoint) m_gipBottomRight;

// GIT cookie – bottom/right

// «закладка» GIT для нижнего/правого элемента

:

:

:

}

Для инициализации элемента GlobalInterfacePointer разработчик (который выполняется в апартаменте объекта) просто регистрирует обрабатываемые указатели, вызывая метод Globalize на каждый GlobalInterfacePointer:

SafeRect::SafeRect(void) : m_cRef (0), m_pUnkFTM(0) {

IPoint *pPoint = 0;

// create instance of class Point

// создаем экземпляр класса Point

HRESULT hr = CoCreateInstance(CLSID_Point, 0, CLSCTX_INPROC, IID_Ipoint, (void**)&pPoint);