Сущность технологии СОМ. Библиотека программиста
Шрифт:
Похоже на то, что по меньшей мере один из методов Rect использует в своей работе два интерфейсных указателя в качестве элементов данных:
STDMETHODIMP Rect::get_Area(long *pn) {
long top, left, bottom, right;
HRESULT hr = m_pPtTopLeft->GetCoords(&left, &top);
assert(SUCCEEDED(hr));
hr = m_pPtBottomRight->GetCoords(&right, &bottom);
assert (SUCCEEDED (hr));
*pn = (right – left) * (bottom – top);
return S_OK;
}
Если бы класс Rect
Это оставляет разработчику Rect выбор между двумя возможностями. Одна из них – не использовать FTM и просто принять к сведению, что когда клиенты передают объектные ссылки Rect между апартаментами, то для обращения к экземплярам класса Rect будет использоваться ORPC. Это действительно является простейшим решением, так как оно не добавляет никакого дополнительного кода и будет работать, не требуя умственных усилий. Другая возможность – не содержать исходные интерфейсные указатели как элементы данных, а вместо этого держать в качестве элементов данных некую маршалированную форму интерфейсного указателя. Именно для этого и предназначена глобальная интерфейсная таблица (Global Interface Table – GIT). Для реализации данного подхода в классе Rect следовало бы иметь в качестве элементов данных не исходные интерфейсные указатели, а «закладку» (cookies) DWORD:
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
//
// где-нибудь в другом месте
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 для двух своих элементов данных: