Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Программы Обои   Экспорт RSS E-Books
 
 

   Программирование -> C/C++ -> Сущность технологии COM


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 выставляет интерфейсы только типа ICar, IPlane, 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 для всех интерфейсов объекта, в действительности очень трудно нарушить требования симметричности, транзитивности и рефлективности.

Реализация также корректно выполняет правило СОМ об идентификации, возвращая только одно значение указателя при запросе IUnknown:

if (riid == IID_IUnknown)
    *ppv = static_cast<ICar*>(this);

Если бы реализация QueryInterface возвращала различные указатели vptr для каждого запроса:

if (riid == IID_IUnknown) { 
    int n = rand() % 3;
    if (n == 0)
        *ppv = static_cast<ICar*>(this);
    else if (n == 1)
        *ppv = static_cast<IBoat*>(this);
    else if (n == 2)
        *ppv = static_cast<IPlane*>(this);
}

то реализация была бы корректной только в терминах чисто С++-отношений типа (то есть все три интерфейса были бы совместимы по типу с запрошенным типом IUnknown). Эта реализация, однако, не является допустимой с точки зрения СОМ, поскольку правило идентификации для QueryInterface было нарушено.

Множественные интерфейсы и имена методов
Динамическая композиция
Двоичная композиция
Включение
Где мы находимся?

 

 
Интересное в сети
 
10 новых программ
CodeLobster PHP Edition 3.7.2
WinToFlash 0.7.0008
Free Video to Flash Converter 4.7.24
Total Commander v7.55
aTunes 2.0.1
Process Explorer v12.04
Backup42 v3.0
Predator 2.0.1
FastStone Image Viewer 4.1
Process Lasso 3.70.4
FastStone Image Viewer 4.0
Xion Audio Player 1.0.125
Notepad GNU v.2.2.8.7.7
K-Lite Codec Pack 5.3.0 Full


Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
Реклама на сайте. Размещая рекламу у нас, вы получите новых посетителей, которые могут стать вашими клиентами.
 
Это интересно
 

Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.