- •7.1. Основные определения
- •7.4. Обработка ошибок
- •7.4.1. Возвращаемое значение типа hresult
- •7.4.2. Возврат информации об ошибках
- •8. Стандарт бинарного интерфейса com
- •8.1. Основные определения
- •8.2. Доступ к com-объектам, работа с памятью
- •8.3. Интерфейс iUnknown и наследование интерфейсов
- •8.3.1. Интерфейс iUnknown
- •8.3.2. Получение указателя на интерфейс – QueryInterface
- •8.3.3. Управление временем жизни объекта – подсчет ссылок
- •8.4. Реализация com
- •9. Управление потоками com
- •9.1. Основные определения
- •9.2. Однопоточные и многопоточные апартаменты (sta и mta)
- •9.2.1. Однопоточные апартаменты (sta)
- •9.2.2. Многопоточный апартамент (mta)
- •9.2.3. Взаимодействие апартаментов
- •9.3. Межпроцессорное взаимодействие
- •9.4. Маршалинг
- •9.4.1. Стандартный маршалинг
- •9.4.2. TypeLib-маршалинг
- •9.4.3. Ручной маршалинг
- •9.5. Заключение
8.3. Интерфейс iUnknown и наследование интерфейсов
8.3.1. Интерфейс iUnknown
Наследование в COM не означает повторного использования кода, как это обычно бывает в объектно-ориентированных языках программирования. Оно означает только, что ассоциированный с интерфейсом контракт наследуется в манере чисто виртуального базового класса С++ и изменяется добавлением новых методов или дальнейшим уточнением допустимого использования методов. В COM отсутствует избирательное наследование. Если один интерфейс наследует от другого, он включает все методы, определяемые другим интерфейсом.
Наследование в стандартных COM-интерфейсах используется довольно скупо. Все интерфейсы должны быть унаследованы от интерфейса IUnknown. IUnknown содержит три жизненно важных для COM метода: QueryInterface, AddRef и Release. Все COM-объекты должны реализовать интерфейс IUnknown, поскольку он обеспечивает возможность получать поддерживаемые объектом интерфейсы с помощью QueryInterface, и управлять сроком жизни объекта через AddRef и Release.
Интерфейс IUnknown инкапсулирует 2 операции:
Управлением временем жизни объекта;
Навигация между объектами.
Высокоуровневые средства разработки (такие, как VB) полностью скрывают реализацию IUnknown, облегчая тем самым работу программиста. Низкоуровневые средства разработки (например, VC++) напротив, дают полный доступ к исходным текстам, реализующим IUnknown. На C++ или Delphi можно самостоятельно реализовать IUnknown. Но проще воспользоваться стандартными реализациями. Для C++ самый удобный, но в тоже время гибкий и очень компактный способ реализации IUnknown – воспользоваться библиотекой ATL (Active Template Library). ATL – это библиотека, главным образом состоящая из набора шаблонов C++. ATL упрощает работу с COM, предоставляя реализации для многих стандартных интерфейсов. Эта библиотека входит в поставку MS VC++ и Borland C++ Builder. В сочетании с визуальными средствами разработки этих сред ATL позволяет очень быстро создавать сложные приложения, базирующиеся на COM.
8.3.2. Получение указателя на интерфейс – QueryInterface
Метод QueryInterface (полный вид – HResult QueryInterface (REEFID riid, void ** ppv)) предоставляет интерфейс IUnknown::QueryInterface COM-объекта, который пытается получить указатель определенного интерфейса. Использование QueryInterface для COM-объекта аналогично выполнению операции приведения в управляемом коде. Вызов объекта с этим методом приводит к увеличению счетчика ссылок для указателя интерфейса до возвращения указателя. Каждый раз по окончании работы с указателем для уменьшения значения счетчика ссылок следует использовать Marshal.Release.
Если возвращаемое значение равно NOERROR, это означает что запрашиваемый интерфейс существует.
Основные правила вызова функции QueryInterface:
Любой вызов QueryInterface через любой интерфейс объекта должен всегда возвращать одну и ту же величину;
После создания объект должен поддерживать все свои интерфейсы;
Любая QueryInterface:
Рефлективна – спецификация СОМ требует, чтобы запрос QueryInterface через интерфейсный указатель всегда достигал цели, если запрошенный тип соответствует типу указателя, с помощью которого произведен запрос (рис. 8.1). Это означает, что QI(A)->A всегда должен быть верным.
Рис. 8.1 Демонстрация рефлективности QueryInterface
Симметрична – спецификация СОМ требует, чтобы, если запрос QueryInterface на интерфейс B удовлетворяется через интерфейсный указатель типа A, то запрос QueryInterface на интерфейс A того же самого объекта через результирующий интерфейсный указатель типа В всегда был успешным (рис. 8.2). Это значит, что если верно QI(A)->B, то также должно быть верным QI(QI(A)->B)->A.
Рис. 8.2 Демонстрация симметричности QueryInterface
Транзитивна – спецификация СОМ требует также, чтобы, если запрос QueryInterface на интерфейс В удовлетворяется через интерфейсный указатель типа A, а второй запрос QueryInterface на интерфейс C удовлетворяется через указатель типа В, то запрос QueryInterface на интерфейс C через исходный указатель типа A был бы также успешным (рис. 8.3). Это означает, что если верно QI(QI(A)->B)->C, то должно быть верным и QI(A)->C.
Рис. 8.2 Демонстрация транзитивности QueryInterface