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);
assert (SUCCEEDED(hr));
// register interface pointer in GIT
// регистрируем интерфейсный указатель в GIT
hr = m_gipTopLeft.Globalize(pPoint);
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_Iроint, (void**) &рРоint);
assert(SUCCEEDED(hr));
// register interface pointer in GIT
// регистрируем интерфейсный указатель в GIT
hr = m_gipBottomRight.Globalize(pPoint);
assert (SUCCEEDED (hr));
pPoint->Release();
// reference is now held in GIT
// теперь ссылка хранится в GIT
}
Те методы, которым нужен доступ к глобализованным указателям, могут импортировать локальную копию посредством метода Localize из GlobalInterfaсePointer :
STDMETHODIMP SafeRect::get_Top(long *pVal) {
IPoint *pPoint = 0;
// local imported pointer
// локальный импортированный указатель
HRESULT hr = m_gipTopLeft.Localize(&pPoint);
if (SUCCEEDED(hr)){
long x;
hr = pPoint->get_Coords(&x, pVal);
pPoint->Release(); }
return hr;
}
Отметим, что в силу применения маршалера свободной поточной обработки ( FreeThreaded Marshaler ) исходный интерфейсный указатель не может быть кэширован, а должен импортироваться при каждом вызове метода, чтобы предотвратить попытку доступа из неверного апартамента.
Предыдущий фрагмент кода может быть автоматизирован еще больше. Поскольку большинство вызовов методов в классе GlobalInterfacePointer должны будут локализовать временный указатель в самом вызове метода, то приводимый ниже класс автоматизирует импорт временного указателя и его последующее освобождение, что очень напоминает интеллектуальный указатель ( smart pointer ):
template class LocalInterfacePointer {
Itf *m_pItf;
// temp imported pointer
// временный импортированный указатель
// prevent misuse
// предотвращаем неверное использование
LocalInterfacePointer(const LocalInterfacePointer&);
operator = (const LocalInterfacePointer&);
public:
LocalInterfacePointer(const GlobalInterfacePointer& rhs, HRESULT& hr) { hr = rhs.Loca1ize(&m_pItf) ; }
LocalInterfacePointer(DWORD dwCookie, HRESULT& hr) { assert(g_pGIT != 0);
hr = g_pGIT->GetInterfaceFromGlobal(dwCookie, *piid, (void**)&m_pItf); }
~LocalInterfacePointer(void) { if (m_pItf) m_pItf->Release(); }
class SafeItf : public Itf { STDMETHOD_(ULONG, AddRef) (void) = 0;
// hide
// скрытый STDMETHOD_(ULONG, Release)(void) = 0;
// hide
// скрытый
};
SafeItf *GetInterface(void) const { return (SafeItf*) m_pItf; }
SafeItf *operator ->(void) const { assert(m_pItf != 0);
return GetInterface();
}
};
#def1ne LIP(Itf) LocalInterfacePointer
С получением этого второго класса C++ обработка импортированных указателей становится намного проще:
STDMETHODIMP SafeRect::get_Area(long *pn) {
long top, left, bottom, right;
HRESULT hr, hr2;
// import pointers
// импортируем указатели
LIP(IPoint) lipTopLeft(m_gipTopLeft, hr);
LIP(IPoint) lipBottomRight(m_gipBottomRight, hr2);
assert(SUCCEEDED(hr) && SUCCEEDED(hr2));
// use temp tocal pointers
// используем временные локальные указатели
hr = lipTopLeft->GetCoords(&left, &top);
hr2 = lipBottomRight->GetCoords(&right, &bottom);
assert(SUCCEEDED(hr) && SUCCEEDED(hr2));
*pn = (right – left) * (bottom – top); return S_OK;
// LocalInterfacePointer auto-releases temp ptrs.
// LocalInterfacePointer сам освобождает
// временные указатели
}
Макросы GIP и LIP делают совместное использование GIT и FTM намного менее громоздким. До появления GIT использование FTM в классе с интерфейсными указателями было значительно более трудным, чем теперь обеспечивает любой из кодов, приведенных в данном разделе.
В данной главе была описана абстракция апартаментов как логическое группирование объектов, которые подчиняются правилам параллелизма и реентерабельности. Процессы имеют один или более апартаментов. Потоки выполняются в ровно одном апартаменте, а для реализации межапартаментных связей СОМ поддерживает маршалинг объектных ссылок через границы апартаментов. Заместитель является локальным представителем объекта, постоянно находящимся в другом апартаменте. Стандартные заместители для передачи запросов методов с удаленного объекта используют ORPC. Специальные заместители имеют полную свободу для обеспечения корректной семантики. Апартамент является фундаментальной абстракцией, которая используется во всей архитектуре удаленного доступа модели СОМ.
Читать дальше