Современная электронная библиотека ModernLib.Net

Сущность технологии СОМ. Библиотека программиста

ModernLib.Net / Программирование / Бокс Дональд / Сущность технологии СОМ. Библиотека программиста - Чтение (стр. 20)
Автор: Бокс Дональд
Жанр: Программирование

 

 



STDMETHODIMP CustStd::GetMarshalSizeMax(

ULONG *pcb, REFIID riid, void *pv, DWORD dwDestCtx, void *pvDestCtx, DWORD mshlflags) {

// if context is supported, do work!

// если контекст поддерживается, то действуем!

if (dwDestCtx == MSHCTX_LOCAL || dwDestCtx == MSHCTX_INPROC) return this->MyCustomMarshalingRoutine(pcb);

// unsupported context, delegate to std marshal

// контекст не поддерживается, обращаемся к стандартному маршапингу

IMarshal *pMsh = 0;

HRESULT hr = CoGetStandardMarshal (riid, pv, dwDestCtx, pvDestCtx, mshlflags, &pMsh);

if (SUCCEEDED(hr)) {

hr = pMsh->GetMarshalSizeMax(pcb, riid, pv, dwDestCtx, pvDestCtx, mshlflags);

pMsh->Retease();

}

return hr;

}


В этом фрагменте кода не показано, как писать инициализационное сообщение для случая, когда действительно желателен специальный маршалинг. Дело в том, что не существует стандартной реализации каждого из методов IMarshal (отсюда и термин специальный (custom) маршалинг). Существует, однако, несколько общих сценариев, в которых специальный маршалинг чрезвычайно выигрышен и реализация IMarshal в этих сценариях – довольно обычное явление. Безусловно, наиболее общим приложением IMarshal является реализация маршалинга по значению (marshal-by-value).

Маршалинг по значению наиболее удобен для таких объектов, которые после инициализации никогда не изменяют своего состояния. Обертки СОМ для структур – вот типичный пример объекта, который просто инициализирован, передан другому объекту для запроса и затем уничтожен. Такой объект является первым кандидатом для специального маршалинга. При реализации маршалинга по значению реализация объекта почти всегда является внутрипроцессным сервером. Это позволяет объекту и заместителю разделять один и тот же класс реализации. Идея маршалинга по значению состоит в том, что специальный заместитель становится клоном исходного объекта. Из этого следует, что маршалированная объектная ссылка должна содержать все состояние исходного объекта, а также (для простоты) то, что CLSID специального заместителя должен быть тем же, что и у исходного объекта.

Представим следующее определение класса СОМ-обертки вокруг простой двумерной точки:


class Point : public IPoint, public IMarshal

{

long m_x;

long m_y;

public:

Point(void) : m_x(0), m_y(0) {}

IMPLEMENT_UNKNOWN (Point)

BEGIN_INTERFACE_TABLE(Point)

IMPLEMENTS_INTERFACE(IPoint)

IMPLEMENTS_INTERFACE(IMarshal)

END_INTERFACE_TABLE()

// IPoint methods

// методы IPoint

// IMarshal methods

// методы IMarshal

};


Для поддержки маршалинга по значению метод MarshalInterface класса должен преобразовать состояние объекта в последовательную форму в качестве инициализационного сообщения для заместителя:


STOMETHODIMP Point::MarshalInterface(IStream *pStm, REFIID, void *, DWORD, void *, DWORD)

{

// write out endian header

// переписываем завершающий заголовок

DWORD dw = OxFF669900;

HRESULT hr = pStm->Write(&dw, sizeof(DWORD), 0);

if (FAILED(hr)) return hr; dw = m_x;

hr = pStm->Write(&dw, sizeof(DWORD), 0);

if (FAILED(hr)) return hr; dw = m_y;

return pStm->Write(&dw, sizeof (DWORD), 0);

}


Если допустить, что класс объекта реализован как внутрипроцессный сервер, то специальный заместитель может стать просто вторым экземпляром того же класса, из чего вытекает следующая реализация GetUnmarshalClass:


STDMETHODIMP Point::GetUnmarshalClass(REFIID, void *, DWORD, void *, DWORD, CLSID *pclsid)

{

*pclsid = CLSID_Point;

// this class's CLSID

// CLSID этого класса return hr;

}


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


STDMETHODIMP Point::GetMarshalSizeMax(REFIID, void *, DWORD, void *, DWORD, DWORD *pcb)

{

*pcb = 3 * sizeof (DWORD);

// m_x + m_y + header

return hr;

}


Когда маршалированная объектная ссылка демаршалируется с помощью CoUnmarshalInterface, тот факт, что она была маршалирована специальным образом, вызовет создание нового специального заместителя. Объектная ссылка содержит CLSID специального заместителя, возвращенный исходным объектом в своем методе GetUnmarshalClass. Когда создан новый специальный заместитель, его метод UnmarshalInterface получает инициализационное сообщение, которое объект записал в своей реализации MarshalInterface:


STDMETHODIMP Point::UnmarshalInterface(IStream *pStm, REFIID riid, void ** ppv)

{

*ppv = 0;

// read endian header // читаем заключительный заголовок DWORD dw; ULONG cbRead;

HRESULT hr = pStm->Read(&dw, sizeof (DWORD), &cbRead);

if (FAILED(hr) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA; bool bSwapEndian = dw == 0x009966FF;

// read m_x and m_y // читаем m_x и m_y

hr = pStm->Read(&dw, sizeof(DWORD), &cbRead);

m_x = dw; if (FAILED(hr) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA;

hr = pStm->Read(&dw, sizeof(DWORD), &cbRead);

m_y = dw; if (FAILED(hr)) || cbRead != sizeof(DWORD)) return RPC_E_INVALID_DATA;

// byte swap members if necessary

// байт переставляет свои биты, если необходимо

if (bSwapEndian) byteswapdata(&m_x, &m_y);

// return pointer to this object

// возвращаем указатель на этот объект return

this->QueryInterface(riid, ppv);

}


Отметим, что реализация MarshalInterface и UnmarshalInterface должна позаботиться о том, чтобы маршалированное состояние могло читаться на любой платформе. Это означает ручную работу по выравниванию, расстановке байтов и учету различий в размерах типов данных.

Приведенная здесь реализация UnmarshalInterface просто возвращает указатель вновь созданному специальному заместителю. Для простого объекта, маршалированного по значению, это может быть приемлемо. Однако более типичные реализации UnmarshalInterface могут захотеть найти несколько демаршалированных указателей, соответствующих одной и той же идентификационной единице СОМ, и возвратить указатель на заместитель той же единицы, чтобы установить отношение идентичности заместителя объекту. Это может не только сэкономить ресурсы, но также повысить чистоту программы.


Маршалер свободной поточной обработки (FreeThreaded Marshaler)

Если в классе установлена опция ThreadingModel="Both" , то она показывает, что экземпляры класса, а также объект класса могут безопасно находиться в любых апартаментах: STA или МТА. В то же время, согласно правилам СОМ, любой данный экземпляр будет находиться только в одном апартаменте. Если бы разработчик объекта прошел все этапы проверки того, что объект может благополучно находиться в МТА, то в этом случае объекту вообще не нужно было бы заботиться об апартаментах. Одновременный доступ к подобному объекту мог бы быть не только для нескольких потоков внутри МТА, но также от потоков вне МТА (например, от потоков, выполняемых в STA). В то же время клиенты не могут знать, что такой доступ является безопасным для отдельно взятого объекта, поэтому любое совместное использование интерфейсного указателя в нескольких апартаментах должно быть установлено с использованием явной технологии маршалинга. Это означает, что доступ к внутрипроцессному объекту будет осуществляться через ORPC-вызовы, если только вызывающий объект не выполняется в том же самом апартаменте, где был создан объект.

В отличие от клиентов, объекты знают о своих отношениях с апартаментами, о своем параллелизме и реентерабельности. Объекты, удовлетворяющиеся ORPC-запросами при доступе из нескольких апартаментов одного и того же процесса, ведут себя так по умолчанию. А объект, которого не устраивает доступ ORPC, имеет возможность обойти это путем реализации специального маршалинга. Довольно просто использовать специальный маршалинг для обхода администратора заглушек и преобразования исходного указателя на объект в маршалированную объектную ссылку. При использовании этой технологии реализация специального заместителя могла бы просто считывать исходный указатель из маршалированной объектной ссылки и передавать его вызывающему объекту в импортирующем апартаменте. Клиентские потоки по-прежнему передавали бы интерфейсный указатель через границу апартамента с помощью явного или неявного вызова CoMarshalInterface / CoUnmarshalInterface. Однако объект мог бы договориться со специальным заместителем о том, чтобы просто передать исходный указатель нужному объекту. Хотя данная технология безупречно работает для внутрипроцессного маршалинга, она, к сожалению, не приводит к успеху в случае межпроцессного маршалинга. Но, к счастью, реализация объекта может просто обратиться к стандартному маршалеру за другим контекстом маршалинга, отличным от MSHCTX_INPROC.

Поскольку только что описанное поведение является полезным для большого класса объектов, в СОМ предусмотрена агрегируемая реализация IMarshal, выполняющая в точности то, что было описано. Эта реализация называется маршалером свободной поточной обработки (FreeThreaded Marshaler – FTM) и может быть осуществлена с помощью вызова API-функции CoCreateFreeThreadedMarshaler:


HRESULT CoCreateFreeThreadedMarshaler( [in] IUnknown *pUnkOuter, [out] IUnknown **ppUnkInner);


Класс, который желает использовать FTM, просто агрегирует экземпляр либо во время инициализации, либо по требованию при первом запросе QueryInterface об интерфейсе IMarshal . Следующий класс заранее обрабатывает FTM во время построения.


class Point : public IPoint {

LONG m_cRef; IUnknown *m_pUnkFTM;

long m_x; long m_y; Point(void) : m_cRef(0), m_x(0), m_y(0) {

HRESULT hr = CoCreateFreeThreadedMarshaler(this,&m_pUnkFTM);

assert(SUCCEEDED(hr)) ;

}

virtual ~Point(void) { m_pUnkFTM->Release(); }

};


Соответствующая реализация QueryInterface просто запросила бы интерфейс IMarshal из FTM:


STDMETHODIMP Point::QueryInterface(REFIID riid, void **ppv)

{ if (riid == IID_IUnknown || riid == IID_IPoint)

*ppv = static_cast(this);

else if (riid == IID_IMarshal) return m_pUnkFTM->QueryInterface(riid, ppv);

else return (*ppv = 0), E_NOINTERFACE;

((IUnknown* )*ppv)->AddRef();

return S_OK;

}


Поскольку используется FTM, не понадобится никаких заместителей, как бы ни маршалировались через внутрипроцессные границы апартамента ссылки на объекты Point . Это применимо к явным вызовам CoMarshalInterface / CoUnmarshalInterface, а также в случаях, когда ссылки на объекты Point передаются как параметры метода на внутрипроцессные заместители объектов, не являющихся объектами Point.

FTM занимает не менее 16 байт памяти. Поскольку многие внутрипроцессные объекты никогда не используются за пределами своего апартамента, то предварительное выделение памяти для FTM не является лучшим использованием имеющихся ресурсов. В высшей степени вероятно, что объект уже имеет некий примитив для синхронизации потоков. В таком случае FTM может быть отложенно агрегирован (lazy-aggregated) при первом же запросе QueryInterface о IMarshal. Для того чтобы добиться этого, рассмотрим такое определение класса:


class LazyPoint : public IPoint {

LONG m_cRef; IUnknown *m_pUnkFTM;

long m_x;

long m_y;

LazyPoint (void) : m_cRef (0) .m_pUnkFTM(0),m_x(0), m_y(0) {}

virtual ~LazyPoint(void) {

if (m_pUnkFTM) m_pUnkFTM->Release();

}

void Lock(void);

// acquire object-specific lock

// запрашиваем блокировку, специфическую для объектов

void Unlock(void);

// release object-specific lock

// освобождаем блокировку, специфическую для объектов

:

:

:

};


Основываясь на таком определении класса, следующая реализация QueryInterface осуществит корректное агрегирование FTM по требованию:


STDMETHODIMP Point::QueryInterface(REFIID riid, void **ppv) {

if (riid == IID_IUnknown || riid == IID_IPoint)

*ppv = static_cast(this);

else if (riid == IID_IMarshal) {

this->Lock();

HRESULT hr = E_NOINTERFACE;

*ppv = 0;

if (m_pUnkFTM == 0)

// acquire FTM first time through

// получаем первый FTM

CoCreateFreeThreadedMarshaler(this, &m_pUnkFTM);

if (m_pUnkFTM != 0)

// by here, FTM is acquired

// здесь получен FTM

hr = m_pUnkFTM->QueryInterface(riid, ppv);

this->Unlock();

return hr;

} else return (*ppv = 0), E_NOINTERFACE;

((IUnknown *)*ppv)->AddRef(); return S_OK; }


Недостатком данного подхода является то, что все запросы QueryInterface на IMarshal будут сериализованы (преобразованы в последовательную форму); тем не менее, если IMarshal вообще не будет запрошен, то будет запрошено меньше ресурсов.

Теперь, когда мы убедились в относительной простоте использования FTM, интересно обсудить случаи, в которых FTM не годится. Конечно, те объекты, которые могут существовать только в однопотоковых апартаментах, не должны использовать FTM, так как маловероятно, что они будут ожидать одновременного обращения к ним. В то же время объекты, способные работать в апартаментах МТА, отнюдь не обязаны использовать FTM. Рассмотрим следующий класс, который использует для выполнения своих операций другие СОМ-объекты:


class Rect : public IRect { LONG m_cRef; IPoint *m_pPtTopLeft; IPoint *m_pPtBottomRight; Rect(void) : m_cRef(0) {

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

assert(SUCCEEDED (hr)); hr = CoCreateInstance(CLSID_Point, 0, CLSCTX_INPROC, IID_Ipoint, (void**)&m_pPtBottomRight);

assert (SUCCEEDED(hr));

}

;

;

;

}


Пусть класс Rect является внутрипроцессным и помечен как ThreadingModel = «Both». Разработчик данного Rect -объекта всегда будет выполняться в апартаменте потока, вызывающего CoCreateInstance (CLSID_Rect). Это означает, что два вызова CoCreateInstance (CLSID_Point) будут также выполняться в апартаменте клиента. Правила же СОМ гласят, что элементы данных m_pPtTopLeft и m_pPtBottomRight могут быть доступны только из того апартамента, который выполняет вызовы CoCreateInstance.

Похоже на то, что по меньшей мере один из методов 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 должен был использовать FTM, тогда можно было бы вызывать этот метод из апартаментов, отличных от того апартамента, который осуществлял начальные вызовы CoCreateInstance. К сожалению, это заставило бы метод get_Area нарушить правила СОМ, поскольку два элемента данных – интерфейсные указатели – являются легальными только в исходном апартаменте. Если бы класс Point также использовал FTM, то формально это не было бы проблемой. Тем не менее, в общем случае клиенты (такие, так класс Rect), не должны делать допущений относительно этой специфической исключительно для реализаций детали. Фактически, если объекты Point не используют FTM и окажутся созданными в другом апартаменте из-за несовместимости с ThreadingModel, то в этом случае объект Rect содержал бы указатели на заместители. Известно, что заместители четко следуют правилам СОМ и послушно возвращают RPC_E_WRONG_THREAD в тех случаях, когда к ним обращаются из недопустимого апартамента.

Это оставляет разработчику 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

// допустим, что указатель на 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);

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


  • Страницы:
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33