Сущность технологии СОМ. Библиотека программиста
Шрифт:
[uuid(75DA645E-DD0F-11d0-8C58-0080C73925BA), object]
interface IShutdownSource : IUnknown {
HRESULT Advise([in] IShutdownNotify *psn, [out] DWORD *pdwCookie);
HRESULT Unadvise([in] DWORD dwCookie);
}
Данный интерфейс существует, однако, только для того, чтобы дать наблюдателям (observers)
Точки стыковки являются идиомой СОМ, предназначенной для регистрации связи интерфейсов обратного вызова с объектом и ее отмены. Точки стыковки не являются необходимыми для создания сетей из объектов с большим количеством соединений. К тому же точки стыковки не обеспечивают двунаправленных соединений. Вместо этого идиома точек стыковки выражает общую концепцию регистрации экспортируемых интерфейсов как небольшого числа интерфейсов стандартной инфраструктуры. Наиболее фундаментальным из этих интерфейсов является IConnectionPoint:
[object, uuid(B196B286-BAB4-101A-B69C-00AA00341D07)]
interface IConnectionPoint : IUnknown {
// which type of interface can be connected
// какой тип интерфейса можно присоединить
HRESULT GetConnectionInterface( [out] IID * pIID);
// get a pointer to identity of «real» object
// получаем указатель на копию «реального» объекта
HRESULT GetConnectionPointContainer([out] IConnectionPointContainer ** ppCPC);
// hold and use pUnkSink until notified otherwise
// сохраняем и используем pUnkSink, пока не объявлено другое
HRESULT Advise([in] IUnknown * pUnkSink, [out] DWORD * pdwCookie);
// stop holding/using the pointer associated with dwCookle
// прекращаем хранение/использование указателя, связанного с dwCookie
HRESULT Unadvise([in] DWORD dwCookie);
// get information about currently held pointers
// получаем информацию об имеющихся в данный момент указателях
HRESULT EnumConnections([out] IEnumConnections ** ppEnum);
}
Как показано на рис. 7.9, объекты представляют отдельную реализацию этого интерфейса каждому типу интерфейса, который может быть использован объектом в качестве интерфейса обратного вызова. Ввиду того, что IConnectionPoint не выставлен как часть единицы идентификации объекта, он не может быть обнаружен посредством QueryInterface. Вместо этого в СОМ предусмотрен второй интерфейс, который выставлен как часть единицы идентификации объекта, которая позволяет клиентам запрашивать реализацию IConnectionPoint, соответствующую отдельному типу интерфейса обратного вызова:
[object,uuid(B196B284-BAB4-101A-B69C-00AA00341D07)]
interface IConnectionPointContainer : IUnknown {
// get all possible IConnectionPoint implementations
//
HRESULT EnumConnectionPoints([out] IEnumConnectionPoints ** ppEnum);
// get the IConnectionPoint implementation for riid
// получаем реализацию IConnectionPoint для riid
HRESULT FindConnectionPoint([in] REFIID riid, [out] IConnectionPoint ** ppCP);
}
Как показано на рис. 7.9, каждая реализация IConnectionPoint выставляется из отдельной СОМ-единицы идентификации.
С учетом вышеупомянутых определений интерфейса клиент мог бы связать свою реализацию IShutdownNotify с объектом следующим образом:
HRESULT HookupShutdownCallback(IUnknown *pUnkObject,
IShutdownNotify *pShutdownNotify,
DWORD &rdwCookie)
{
IConnectionPointContainer *pcpc = 0;
HRESULT hr = pUnkObject->QueryInterface(IID_IConnectionPointContainer, (void**)&pcpc);
if (SUCCEEDED(hr)) {
IConnectionPoint *pcp = 0;
hr =pcpc->FindConnectionPoint(IID_IShutdownNotify,&pcp);
if (SUCCEEDED(hr)) {
hr = pcp->Advise(pShutdownNotify, &rdwCookie);
pcp->Release;
}
pcpc->Release;
}
}
Соответствующий код для разрыва связи выглядит так:
HRESULT TeardownShutdownCallback(IUnknown *pUnkObject, DWORD dwCookie)
{
IConnectionPointContainer *pcpc = 0;
HRESULT hr = pUnkObject->QueryInterface(IID_IConnectionPointContainer, (void**)&pcpc);
if (SUCCEEDED(hr)) {
IConnectionPoint *pcp = 0;
hr =pcpc->FindConnectionPoint(IID_IShutdownNotify,&pcp);
if (SUCCEEDED(hr)) {
hr = pcp->Unadvise(dwCookie);
pcp->Release;
}
pcpc->Release;
}
}
Отметим, что в обоих примерах клиент использует метод IConnectionPointContainer::FindConnectionPoint для вызова из объекта его IShutdownNotify-реализации IConnectionPoint. Если объект отклоняет вызов FindConnectionPoint, это говорит о том, что он не понимает семантику интерфейса IShutdownNotify. Это оберегает пользователя от прикрепления произвольных интерфейсов обратного вызова к объекту без полного согласия на это разработчика объекта.
Как и в случае с IUnknown, реализации IConnectionPointContainer и IConnectionPoint в значительной степени типичны. Объекту C++ требуется отдельная единица идентификации СОМ для каждого типа экспортируемого интерфейса, который он предполагает поддерживать. Одна из методик реализации ConnectionPoint состоит в использовании того варианта методики вложения класса/композиции, которая учитывает различия в отношениях тождественности:
class Surfboard : public ISurfboard,
public IHazardousDevice,