Сущность технологии СОМ. Библиотека программиста
Сущность технологии СОМ. Библиотека программиста читать книгу онлайн
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
BOOL bIsDone = FALSE;
while (!bIsDone && SUCCEEDED(hr)) {
Sleep(15000);
// wait 15 seconds
// ожидаем 15 секунд
hr = pp->IsProductDone(&bIsDone);
// check status
// проверяем состояние
}
pp->Release();
}
}
Очевидно, что этот код весьма неэффективен, поскольку клиент каждые 15 секунд опрашивает состояние объекта. Более эффективным для клиента был бы следующий подход: подготовить второй объект, которому объект-программист (programmer object) мог бы сообщить, когда данный объект придет в нужное состояние. Этот подготовленный клиентом объект должен экспортировать интерфейс, предоставляющий контекст, через который мог бы работать объект-программист:
[uuid(75DA6458-DD9F-11d0-8C58-0080C73925BA),object]
interface ISoftwareConsumer : IUnknown {
HRESULT OnProductIsDone(void);
HRESULT OnProductWillBeLate([in] hyper nMonths);
}
При таком парном определении интерфейса должен существовать некий механизм для информирования объекта-программиста о том, что у клиента имеется реализация ISoftwareConsumer, с помощью которой он может получать уведомления от объекта-программиста об изменениях состояния. Одной из распространенных методик является определение IProgrammer таким образом, чтобы он имел явные методы, через которые клиенты могли бы связываться со своими объектами-потребителями (consumer object). Канонической формой этой идиомы является включение метода Advise:
interface IProgrammer : IUnknown {
HRESULT Advise ([in] ISoftwareConsumer *psc, [out] DWORD *pdwCookie);
: : :
посредством которого клиент подготавливает парный объект-потребитель, а программист возвращает DWORD для подтверждения связи. Затем этот DWORD можно было бы использовать в соответствующем методе Unadvise:
interface IProgrammer : IUnknown {
: : :
HRESULT Unadvise([in] DWORD dwCookie);
}
для того, чтобы сообщить объекту-программисту о прерывании связи. При использовании уникальных DWORD для представления связи программист-потребитель дизайн интерфейса позволяет произвольному числу потребителей независимо друг от друга соединяться с объектом и отсоединяться от него.
Если эти два метода имеются в интерфейсе IProgrammer, то реализация программиста может быть соединена с объектом-потребителем с помощью метода Advise
STDMETHODIMP Programmer::Advise(ISoftwareConsumer *pcs, DWORD *pdwCookie)
{
assert(pcs);
if (m_pConsumer != 0) // is there already a consumer?
// уже есть потребитель?
return E_UNEXPECTED;
(m_pConsumer = pcs)->AddRef();
// hold onto new consumer
// соединяемся с новым потребителем
*pdwCookie = DWORD(pcs);
// make up a reasonable cookie
// готовим подходящий маркер
return S_OK;
}
Соответствующая реализация метода Unadvise выглядела бы примерно так:
STDMETHODIMP Programmer::Unadvise(DWORD dwCookie)
{
// does the cookie correspond to the current consumer?
// соответствует ли маркер данному потребителю?
if (DWORD(m_pConsumer) != dwCookie)
return E_UNEXPECTED;
(m_pConsumer)->Release();
// release current consumer
// освобождаем текущего потребителя
m_pConsumer = 0;
return S_OK;
}
Взаимоотношения между программистом и потребителем показаны на рис. 7.7. Хотя в данной реализации в каждый момент предусмотрен только один потребитель, возможно, что более опытный программист смог бы оперировать несколькими потребителями одновременно, управляя динамическим массивом интерфейсных указателей из ISoftwareConsumer.
Имея приведенную выше реализацию, метод программиста StartHacking может теперь использовать потребителя для индикации готовности результата:
STDMETHODIMP Programmer::StartHacking (void)
{
assert(m_pConsumer);
// preemptively notify of lateness
// приоритетно сообщаем о задержке
HRESULT hr = m_Consumer->OnProductWillBeLate(3);
if (FAILED(hr))
return PROGRAMMER_E_UNREALISTICCONSUMER;
// generate some code
// генерируем некоторый код
extern char *g_rgpszTopFiftyStatements[];
for (int n = 0; n < 100000; n++)
printf(g_rgpszTopFiftyStatements[rand() % 50]);
// inform consumer of done-ness
// извещаем потребителя о выполнении
hr = m_pConsumer->OnProductIsDone();
return S_OK;
}
To обстоятельство, что реализация ISoftwareConsumer может принадлежать к другому апартаменту, чем объект-программист, не является существенным. На самом деле метод StartHacking может быть вызван из того апартамента, который содержит объект-потребитель, и в этом случае будет осуществлено повторное вхождение в апартамент вызывающей программы, что, в сущности, является синхронным обратным вызовом. В то время как эта реализация делает вложенные вызовы на объект-потребитель, объект-программист может также в будущем производить вызовы методов объекта-потребителя в любое время. Эта привилегия остается до тех пор, пока не последует вызов метода Unadvise, разрывающий соединение.
Поскольку интерфейсы IProgrammer и ISoftwareConsumer, вероятно, были созданы в тандеме для совместной работы, использование явного метода интерфейса IProgrammer для установления связи становится частью протокола при работе с объектами-программистами и является вполне целесообразным. Тот факт, что реализации программиста способны использовать один или несколько объектов-потребителей, может быть документирован как часть протокола интерфейса IProgrammer в порядке уточнения семантического контракта IProgrammer. Существуют, однако, сценарии, в которых интерфейсы совместной работы и обратного вызова разработаны так, что они находятся вне области видимости любого другого интерфейса. Ниже приведен пример такого интерфейса:
[uuid(75DA645D-DD0F-11d0-8C58-0080C73925BA),object ]
interface IShutdownNotify : IUnknown {
HRESULT OnObjectDestroyed([in] IUnknown *pUnk);
}
В этом интерфейсе предполагается, что разработчик IShutdownNotify заинтересован в получении сообщений о прекращении работы от других объектов. В данном определении, однако, не приведен механизм, с помощью которого эти заинтересованные стороны могли бы сообщить объектам, что они хотели бы быть уведомлены об уничтожении этого объекта. Как показано на рис. 7.8, одна из возможных стратегий осуществления этого состоит в определении второго (парного) интерфейса, который объекты могли бы реализовать:
[uuid(75DA645E-DD0F-11d0-8C58-0080C73925BA), object]
interface IShutdownSource : IUnknown {
HRESULT Advise([in] IShutdownNotify *psn, [out] DWORD *pdwCookie);
HRESULT Unadvise([in] DWORD dwCookie);
}
Данный интерфейс существует, однако, только для того, чтобы дать наблюдателям (observers) возможность соединить свои интерфейсы IShutdownNotify с объектом. Если имеется большое число типов интерфейсов обратного вызова, то необходимо определить столь же большое число соответствующих интерфейсов только для управления соединением. Ясно, что должен существовать более общий механизм: вхождение в точках стыковки.
Точки стыковки являются идиомой СОМ, предназначенной для регистрации связи интерфейсов обратного вызова с объектом и ее отмены. Точки стыковки не являются необходимыми для создания сетей из объектов с большим количеством соединений. К тому же точки стыковки не обеспечивают двунаправленных соединений. Вместо этого идиома точек стыковки выражает общую концепцию регистрации экспортируемых интерфейсов как небольшого числа интерфейсов стандартной инфраструктуры. Наиболее фундаментальным из этих интерфейсов является IConnectionPoint: