Интерфейсы – основа СОМ-технологии
Второй параметр функции Queryinterfасе (указатель на указатель) позволяет возвратить в вызывающую функцию адрес запрашиваемого интерфейса. Примерная схема реализации метода Queryinterfасе (в классе СОМ-объекта, производном от IМу) может иметь такой вид:
HRESULT _ stdcall СМу::Queryinterfасе(REFIID id, void **ppv) { //=== В *ppv надо записать адрес искомого интерфейса //=== Пессимистический прогноз (интерфейс не найден) *ppv = 0; // Допрашиваем REFIID искомого интерфейса. Если он // нам не знаком, то вернем отказ E_NOINTERFACE // Если нас не знают, но хотят познакомиться, // то возвращаем свой адрес, однако приведенный // к типу "неизвестного" родителя if (id == IID_IUnknown) *ppv = static_cast<IUnknown*>(this); // Если знают, то возвращаем свой адрес приведенный // к типу "известного" родителя IМу else if (id == IID_IMy) *ppv = static_cast<IMy*>(this); //===== Иначе возвращаем отказ else return E_NOINTERFACE; //=== Если вопрос был корректным, то добавляем единицу //=== к счетчику наших пользователей AddRef(); return S_OK; }
Методы AddRef и Release управляют временем жизни объектов посредством подсчета ссылок (references) на пользователей интерфейса. В соответствии с общей концепцией объект (или его интерфейс) не может быть выгружен системой из памяти, пока не равен нулю счетчик ссылок на его пользователей. При создании интерфейса в счетчик автоматически заносится единица. Каждое обращение к AddRef увеличивает счетчик на единицу, а каждое обращение к Release – уменьшает. При обнулении счетчика объект уничтожает себя сам. Например, так:
ULONG СМу::Release() { //====== Если есть пользователи интерфейса if (– m_Ref!= 0) return m_Ref; // Возвращаем их число delete this; // Если нет – уходим из жизни, // освобождая память return 0; }
Вы, наверное, заметили, что появилась переменная m_Ref. Ранее было сказано об отсутствии переменных у интерфейсов. Интерфейсы – это голая функциональность. Но обратите внимание на тот факт, что метод Release принадлежит не интерфейсу 1Му, а классу ему, в котором переменные естественны. Обычно в классе СОМ-объекта и реализуются чисто виртуальные методы всех интерфейсов, в том числе и главного интерфейса zunknown. Класс ему обычно создает разработчик СОМ-объекта и производит его от желаемого интерфейса, например, так:
class СМу: public IMy { // Данные и методы класса, // в том числе и методы lUnknown };
В свою очередь, интерфейс IMy должен иметь какого-то родителя, может быть, только iUnknown, а может быть одного из его потомков, например:
interface IMy: IClassFactory { // Методы интерфейса };
СОМ-объектом считается любой объект, поддерживающий хотя бы lUnknown. Историческое развитие С ОМ-технологий определило многообразие терминов типа: OLE 94, OLE-2, OCX-96, OLE Automation и т. д. Элементы ActiveX принадлежат к той же группе СОМ-объектов. Каждый новый термин из этой серии подразумевает все более высокий уровень предоставляемых интерфейсов. Элементы ActiveX должны как минимум обладать способностью к активизации на месте, поддерживать OLE Automation, допуская чтение и запись своих свойств, а также вызов своих методов.