Правила реализации QueryInterface

Существует три основных правила, которые управляют реализацией метода IUnknown::QueryInterface в com-объекте:

  • Объекты должны иметь удостоверение.
  • Набор интерфейсов экземпляра объекта должен быть статическим.
  • Запрос к любому интерфейсу объекта из любого другого интерфейса должен быть возможным.

Объекты должны иметь удостоверение

Для любого экземпляра объекта вызов QueryInterface с IID_IUnknown всегда должен возвращать одно и то же физическое значение указателя. Это позволяет вызывать QueryInterface на всех двух интерфейсах и сравнивать результаты, чтобы определить, указывают ли они на один и тот же экземпляр объекта.

Набор интерфейсов экземпляра объекта должен быть статическим

Набор интерфейсов, доступных для объекта через QueryInterface , должен быть статическим, а не динамическим. В частности, если QueryInterface возвращает S_OK для заданного IID один раз, он никогда не должен возвращать E_NOINTERFACE при последующих вызовах того же объекта; если QueryInterface возвращает E_NOINTERFACE для заданного IID, последующие вызовы того же IID для одного и того же объекта никогда не должны возвращать S_OK.

Запрос к любому интерфейсу объекта из любого другого интерфейса должен быть возможным.

То есть, учитывая следующий код:

IA * pA = (some function returning an IA *); 
IB * pB = NULL; 
HRESULT   hr; 
hr = pA->QueryInterface(IID_IB, &pB); 
 

Применяются следующие правила:

  • Если у вас есть указатель на интерфейс в объекте, вызов, как показано ниже , в QueryInterface для этого же интерфейса должен выполниться успешно:

    pA->QueryInterface(IID_IA, ...) 
    
    
  • Если вызов QueryInterface для второго указателя интерфейса выполнен успешно, вызов QueryInterface из этого указателя должен также завершиться успешно. Если pB успешно получен, то следующее также должно быть успешно выполнено.

    pB->QueryInterface(IID_IA, ...) 
    
    
  • Любой интерфейс должен иметь возможность запрашивать любой другой интерфейс в объекте. Если pB успешно получен и вы успешно запрашиваете третий интерфейс (IC) с помощью этого указателя, необходимо также иметь возможность успешно запрашивать ic с помощью первого указателя pA. В этом случае должна быть выполнена следующая последовательность:

    IC * pC = NULL; 
    hr = pB->QueryInterface(IID_IC, &pC); 
    pA->QueryInterface(IID_IC, ...) 
    
    

Реализации интерфейса должны поддерживать счетчик невыполненных ссылок указателя на все интерфейсы для заданного объекта. Для счетчика следует использовать целое число без знака.

Если клиенту необходимо знать, что ресурсы освобождены, он должен использовать метод в некотором интерфейсе объекта с семантикой более высокого уровня перед вызовом IUnknown::Release.

Использование и реализация IUnknown