Дональд Бокс - Сущность технологии СОМ. Библиотека программиста Страница 38
- Категория: Компьютеры и Интернет / Программирование
- Автор: Дональд Бокс
- Год выпуска: -
- ISBN: -
- Издательство: -
- Страниц: 95
- Добавлено: 2019-05-29 11:55:05
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста краткое содержание
Прочтите описание перед тем, как прочитать онлайн книгу «Дональд Бокс - Сущность технологии СОМ. Библиотека программиста» бесплатно полную версию:В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста читать онлайн бесплатно
void AssertStaticType(IUnknown *pUnk, REFIID riid)
{
IUnknown *pUnk1 = 0,
*pUnk2 = 0;
HRESULT hr1 = pUnk->QueryInterface(riid, (void**)&pUnk1);
HRESULT hr2 = pUnk->QueryInterface(riid, (void**)&pUnk2);
// both requests for the same interface should
// yield the same yes/no answer
// оба запроса того же самого интерфейса
// должны получить тот же самый ответ да/нет
assert(SUCCEEDED(hr1) == SUCCEEDED(hr2));
if (SUCCEEDED(hr1)) pUnk1->Release();
if (SUCCEEDED(hr2)) pUnk2->Release();
}
Это требование означает, что в СОМ запрещены следующие программные технологии:
Использование временной информации при решении вопроса о том, удовлетворять или нет запрос QueryInterface (например, выдавать интерфейс IMorning (утро) только до 12:00).
Использование переменной информации о состоянии при решении вопроса о том, удовлетворять или нет запрос QueryInterface (например, выдавать интерфейс INotBusy (не занят), только если количество внешних интерфейсных указателей меньше десяти).
Использование маркера доступа (security token) вызывающего объекта для решения, удовлетворять или нет запрос QueryInterface . Как будет объяснено в главе 6, на самом деле это не обеспечивает никакой реальной безопасности из-за протокола передачи (wire protocol ), используемого СОМ.
Использование успешного захвата динамических ресурсов для решения вопроса о том, удовлетворять или нет запрос QueryInterface (например, выдавать интерфейс IHaveTonsOfMemory (у меня тонны памяти) только при успешном выполнении malloc(4096*4096)).
Эта последняя методика может быть до некоторой степени смягчена, если разработчик объекта желает поупражняться с выражением спецификации СОМ «barring catastrophic failure» (за исключением катастрофического сбоя).
Эти ограничения не означают, что два объекта одного и того же класса реализации не могут давать различные ответы «да/нет» при запросе одного и того же интерфейса. Например, класс может реализовать показанные ранее интерфейсы ICar, IBoat и IPlane , но может разрешить только одному интерфейсу быть использованным в каком-то определенном объекте. Эти ограничения также не означают, что объект не может использовать постоянную или временную информацию для решения вопроса о том, дать ли исходное «да» или «нет» для данного интерфейса. В примере для класса, который разрешает только один из трех интерфейсов, следующая идиома была бы вполне допустимой:
class СВР : public ICar, public IPlane, public IBoat
{
enum TYPE { CAR, BOAT, PLANE, NONE };
TYPE m_type;
CBP(void) : m_type(NONE) { }
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (md == IID_ICar)
{
// 1st QI Initializes type of object
// первая QI инициализирует тип объекта
if (m_type == NONE) m_type = CAR;
// only satisfy request if this object is a car
// удовлетворяем запрос, только если данный объект
// является car (автомобилем)
if (m_type == CAR) *ppv = static_cast<ICar*>(this);
else return (*ppv = 0), E_NOINTERFACE;
}
else if (md == IID_IBoat)
{
// similar treatment for IBoat and IPlane
// IBoat и IPlane обрабатываются сходным образом
}
};
Из требования, чтобы множество поддерживаемых интерфейсов было статичным, следует простой вывод, что разработчикам объектов не разрешается создавать конструкции, состоящие из одного объекта, который дает два различных ответа «да/нет» на запрос определенного интерфейса. Одна из причин того, что иерархия типов объекта должна оставаться неизменной на всем протяжении своего жизненного цикла, состоит в том, что СОМ не гарантирует отправления всех клиентских запросов QueryInterface такому объекту в случае, когда к нему имеется удаленный доступ. Неизменность иерархии типов позволяет «заместителям» на стороне клиента (client-side proxies) кэшировать результаты QueryInterface во избежание чрезмерных обменов клиент-объект. Такая оптимизация очень важна для эффективности СОМ, но она разрушает конструкции, использующие QueryInterface для передачи динамической семантической информации вызывающему объекту.
Единственность и идентификация
Предыдущий раздел был посвящен запросам QueryInterface, которые представляют собой ответы типа «да/нет» вызывающим объектам. QueryInterface действительно возвращает S_OK (да) или E_NOINTERFACE (нет). Впрочем, когда QueryInterface возвращает S_OK, то он также возвращает объекту интерфейсный указатель. Для СОМ значение этого указателя чрезвычайно важно, так как оно позволяет клиентам определить, действительно ли на один и тот же объект указывают два интерфейсных указателя.
QueryInterface и IUnknown
Свойство рефлективности QueryInterface гарантирует, что любой интерфейсный указатель сможет удовлетворить запросы на IUnknown, поскольку все интерфейсные указатели неявно принадлежат к типу IUnknown. Спецификация СОМ имеет немного больше ограничений при описании результатов запросов QueryInterface именно на IUnknown. Объект не только должен отвечать «да» на запрос, он должен также возвращать в ответ на каждый запрос в точности одно и то же значение указателя. Это означает, что в следующем коде оба утверждения всегда должны быть верны:
void AssertSameObject(IUnknown *pUnk)
{
IUnknown *pUnk1 = 0,
*pUnk2 = 0;
HRESULT hr1 = pUnk->QueryInterface(IID_IUnknown, (void **)&pUnk1);
HRESULT hr2 = pUnk->QueryInterface(IID_IUnknown, (void **)&pUnk2);
// QueryInterface(IUnknown) must always succeed
// QueryInterface(IUnknown) должно всегда быть успешным
assert(SUCCEEDED(hr1) && SUCCEEDED(hr2));
// two requests for IUnknown must always yield the
// same pointer values
// два запроса на IUnknown должны всегда выдавать
// те же самые значения указателя
assert(pUnk1 == pUnk2);
pUnk1->Release();
pUnk2->Release();
}
Это требование позволяет клиентам сравнивать два любых указателя интерфейса для выяснения того, действительно ли они указывают на один и тот же объект.
bool IsSameObject(IUnknown *pUnk1, IUnknown *pUnk2)
{ assert(pUnk1 && pUnk2);
bool bResult = true;
if (pUnk1 != pUnk2)
{
HRESULT hr1, hr2; IUnknown *p1 = 0, *p2 = 0;
hr1 = pUnk1->QueryInterface(IID_IUnknown, (void **)&p1);
assert(SUCCEEDED(hr1));
hr2 = pUnk2->QueryInterface(IID_IUnknown, (void **)&p2);
assert(SUCCEEDED(hr2));
// compare the two pointer values, as these
// represent the identity of the object
// сравниваем значения двух указателей,
// так как они идентифицируют объект
bResult = (р1 == р2); p1->Release();
p2->Release();
}
return bResult;
}
В главе 5 будет рассмотрено, что понятие идентификации является фундаментальным принципом, так как он используется в архитектуре удаленного доступа СОМ с целью эффективно представлять интерфейсные указатели на объекты в сети.
Вооружившись знанием правил IUnknown, полезно исследовать реализацию объекта и убедиться в том, что она придерживается всех этих правил. Следующая реализация выставляет каждый из четырех интерфейсов средств транспорта и IUnknown:
class CarBoatPlane : public ICar, public IBoat, public IPlane
{
public:
// IUnknown methods – методы IUnknown
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IVehicle methods – методы IVehicle
STDMETHODIMP GetMaxSpeed(long *pMax);
// ICar methods – методы
ICar STDMETHODIMP Brake(void);
// IBoat methods – методы
IBoat STDMETHODIMP Sink(void);
// IPlahe methods – методы
IPlane STDMETHODIMP TakeOff(void); };
Ниже приведена стандартная реализация QueryInterface в CarBoatPlane:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown) *ppv = static_cast<ICar*>(this);
else if (riid == IID_IVehicle) *ppv = static_cast<ICar*>(this);
else if (riid == IID_ICar) *ppv = static_cast<ICar*>(this);
else if (riid == IID_IBoat) *ppv = static_cast<IBoat*>(this);
else if (riid == IID_IPlane) *ppv = static_cast<IPlane*>(this);
else return (*ppv = 0), E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
Для того чтобы быть объектом СОМ, реализация CarBoatPlane QueryInterface должна полностью придерживаться правил IUnknown , приведенных в данной главе.
Класс CarBoatPlane выставляет интерфейсы только типа ICarIPlane, IBoat, IVehicle и IUnknown . Каждая таблица vtbl CarBoatPlane будет ссылаться на единственную реализацию QueryInterface, показанную выше. К каждому поддерживаемому интерфейсу можно обращаться через эту реализацию QueryInterface, так что невозможно найти два несимметричных интерфейса, то есть не существует двух интерфейсов A и B, для которых неверно следующее:
If QI(A)->B Then QI(QI(A)->B)->A
Если следовать той же логике, то поскольку все пять интерфейсов принадлежат к одной и той же реализации QueryInterface, не существует трех интерфейсов А, В и С , для которых неверно следующее:
If QI(QI(A)->B)->C Then QI(A)->C
Наконец, поскольку реализация QueryInterface всегда удовлетворяет запросы на пять возможных интерфейсных указателей, которые могут поддерживаться клиентом, то следующее утверждение должно быть верным для каждого из пяти поддерживаемых интерфейсов:
QI(A)->A
Поскольку из множественного наследования вытекает единственная реализация QueryInterface для всех интерфейсов объекта, в действительности очень трудно нарушить требования симметричности, транзитивности и рефлективности.
Жалоба
Напишите нам, и мы в срочном порядке примем меры.