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
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);