- •Оглавление
- •От автора
- •Введение
- •Преимущества использования компонентов
- •Адаптация приложений
- •Библиотеки компонентов
- •Распределенные компоненты
- •Требования к компонентам
- •Динамическая компоновка
- •Инкапсуляция
- •Заключительные замечания о компонентах
- •Повторное использование архитектур приложений
- •Соглашения о кодировании
- •Законченный пример
- •Взаимодействие в обход интерфейсов
- •Детали реализации
- •Теория интерфейсов, часть II
- •Интерфейсы не изменяются
- •Полиморфизм
- •Что за интерфейсом
- •Таблица виртуальных функций
- •Указатели vtbl и данные экземпляра
- •Множественные экземпляры
- •Разные классы, одинаковые vtbl
- •Запрос интерфейса
- •IUnknown
- •Получение указателя на IUnknown
- •Знакомство с QueryInterface
- •Использование QueryInterface
- •Реализация QueryInterface
- •А теперь все вместе
- •Правила и соглашения QueryInterface
- •Вы всегда получаете один и тот же IUnknown
- •Вы можете получить интерфейс снова, если смогли получить его раньше
- •Вы можете снова получить интерфейс, который у Вас уже есть
- •Вы всегда можете вернуться туда, откуда начали
- •Если Вы смогли попасть куда-то хоть откуда-нибудь, Вы можете попасть туда откуда угодно
- •QueryInterface определяет компонент
- •Вы не можете воспользоваться всеми знаниями сразу
- •Работа с новыми версиями компонентов
- •Когда нужно создавать новую версию
- •Имена версий интерфейсов
- •Неявные соглашения
- •Управление временем жизни
- •Подсчет ссылок
- •Подсчет ссылок на отдельные интерфейсы
- •Реализация AddRef и Release
- •Когда подсчитывать ссылки
- •Оптимизация подсчета ссылок
- •Правила подсчета ссылок
- •Амуниция пожарного, резюме
- •Создание компонента
- •Экспорт функции из DLL
- •Загрузка DLL
- •Разбиваем монолит
- •Тексты программ
- •Связки объектов
- •Негибкое связывание, резюме
- •HRESULT
- •Поиск HRESULT
- •Использование HRESULT
- •Определение собственных кодов ошибки
- •GUID
- •Зачем нужен GUID?
- •Объявление и определение GUID
- •Сравнение GUID
- •Передача GUID по ссылке
- •Реестр Windows
- •Организация Реестра
- •Редактор Реестра
- •Необходимый минимум
- •Другие детали Реестра
- •ProgID
- •Саморегистрация
- •Категории компонентов
- •OleView
- •Некоторые функции библиотеки COM
- •Инициализация библиотеки COM
- •Управление памятью
- •Преобразование строк в GUID
- •Резюме
- •CoCreateInstance
- •Прототип CoCreateInstance
- •Использование CoCreateInstance
- •Контекст класса
- •Листинг кода клиента
- •Но CoCreateInstance недостаточно гибка
- •Фабрики класса
- •Использование CoGetClassObject
- •IClassFactory
- •CoCreateInstance vs. CoGetClassObject
- •Фабрики класса инкапсулируют создание компонентов
- •Реализация фабрики класса
- •Использование DllGetClassObject
- •Общая картина
- •Листинг кода компонента
- •Последовательность выполнения
- •Регистрация компонента
- •Несколько компонентов в одной DLL
- •Повторное применение реализации фабрики класса
- •Выгрузка DLL
- •Использование DllCanUnloadNow
- •LockServer
- •Резюме
- •Включение и агрегирование
- •Включение
- •Агрегирование
- •Сравнение включения и агрегирования
- •Реализация включения
- •Расширение интерфейсов
- •Реализация агрегирования
- •Магия QueryInterface
- •Неверный IUnknown
- •Интерфейсы IUnknown для агрегирования
- •Создание внутреннего компонента
- •Законченный пример
- •Слепое агрегирование
- •Агрегирование и включение в реальном мире
- •Предоставление информации о внутреннем состоянии
- •Моделирование виртуальных функций
- •Резюме
- •Упрощения на клиентской стороне
- •Smart-указатели на интерфейсы
- •Классы-оболочки C++
- •Упрощения на серверной стороне
- •Базовый класс CUnknown
- •Базовый класс CFactory
- •Использование CUnknown и CFactory
- •Резюме
- •Разные процессы
- •Локальный вызов процедуры
- •Маршалинг
- •DLL заместителя/заглушки
- •Введение в IDL/MIDL
- •Примеры описаний интерфейсов на IDL
- •Компилятор MIDL
- •Реализация локального сервера
- •Работа примера программы
- •Нет точек входа
- •Запуск фабрик класса
- •Изменения в LockServer
- •Удаленный сервер
- •Что делает DCOMCNFG.EXE?
- •Но как это работает?
- •Другая информация DCOM
- •Резюме
- •Новый способ общения
- •Старый способ общения
- •Использование IDispatch
- •Параметры Invoke
- •Примеры
- •Тип VARIANT
- •Тип данных BSTR
- •Тип данных SAFEARRAY
- •Библиотеки типа
- •Создание библиотеки типа
- •Библиотеки типа в Реестре
- •Реализация IDispatch
- •Генерация исключений
- •Маршалинг
- •Что Вы хотите сделать сегодня?
- •Потоковые модели COM
- •Потоки Win32
- •Подразделение
- •Разделенные потоки
- •Свободные потоки
- •Маршалинг и синхронизация
- •Реализация модели разделенных потоков
- •Автоматический маршалинг
- •Ручной маршалинг
- •Настало время написать программу
- •Пример с разделенным потоком
- •Реализация модели свободных потоков
- •Пример со свободным потоком
- •Оптимизация маршалинга для свободных потоков
- •Информация о потоковой модели в Реестре
- •Резюме
- •Программа Tangram
- •Tangram в работе
- •Детали и составные части
- •Клиентский EXE-модуль
- •Компонент TangramModel
- •Компоненты TangramGdiVisual и TangramGLVisual
- •Компоненты TangramGdiWorld и TangramGLWorld
- •Что демонстрирует пример
- •Файлы IDL
- •Файл DLLDATA.C
- •Циклический подсчет ссылок
- •Не вызывайте AddRef
- •Используйте явное удаление
- •Используйте отдельный компонент
- •События и точки подключения
- •IEnumXXX
103
Обратите внимание, что DllGetClassObject обладает подобными знаниями о создаваемой ею фабрике класса, а IClassFactory::CreateInstance — о создаваемом компоненте. Эти функции изолируют клиент от деталей реализации компонента. Можно сделать эти функции пригодными для повторного использования, однако в том или ином месте им обязательно потребуются специфические знания о том, как создавать конкретную фабрику класса или конкретный компонент. Способы, используемые DllGetClassObject и IClassFactory::CreateInstance для создания компонентов, полностью оставлены на усмотрение разработчика. Повторно применимая реализация
DllGetClassObject и IClassFactory::CreateInstance будет представлена в гл. 9.
Последовательность выполнения
Двайте подробно рассмотрим последовательность выполнения кодов клиента и компонента, приведенных в листингах 7-1 и 7-2. На рис. 7-2 эта последовательность представлена графически. Ось времени на рисунке направлена вниз. Пяти основным структурным элементам — клиенту, библиотеке COM, DLL, фабрике класса и компоненту — соответствуют отдельные колонки. С каждым элементом связана вертикальная линия. Сплошная линия означает, что данный элемент был создан и все еще существует. Пунктирная линия показывает, что элемент еще или уже не существует. Прямоугольники, нанесенные поверх линий, соответствуют временам выполнения операций. Горизонтальные линии — это вызовы функций, передающие управление от одного структурного элемента к другому.
Время
Клиент |
Библиотека COM |
DLL |
Фабрика класса |
Компонент |
CoCreateInstance CoGetClassObject
DllGetClassObject
new CFactory
IClassFactory::CreateInstance(IID_IX)
new CA
IClassFactory::Release
pIX->Fx()
Рис. 7-2 CoCreateInstance позаботится за клиента о множестве мелких деталей создания компонента
Для простоты я опустил вызовы членов IUnknown и другие мелкие подробности. Кратко рассмотрим содержание рисунка. Сначала клиент вызывает CoCreateInstance, которая реализована в библиотеке СОМ. CoCreateInstance реализована с помощью CoGetClassObject. CoGetClassObject отыскивает компонент в Реестре. Если компонент найден, то CoGetClassObject загружает DLL, являющуюся сервером компонента. После загрузки DLL
CoGetClassObject вызывает DllGetClassObject. DllGetClassObject реализована DLL-сервером. Ее задача — создать фабрику класса, что делается в данном примере при помощи оператора new С++. Кроме того, DllGetClassObject запрашивает у фабрики класса интерфейс IClassFactory, который возвращается CoCreateInstance. Последняя вызывает метод CreateInstance этого интерфейса. В нашем примере IClassFactory::CreateInstance использует для создания компонента оператор new. Кроме того, она запрашивает у компонента интерфейс IX. Получив его, CoCreateInstance освобождает фабрику класса и возвращает указатель на IX клиенту. Затем клиент может использовать данный указатель для вызова методов компонента. Проще некуда.
Регистрация компонента
Компоненты DLL экспортируют четыре функции. Мы уже рассмотрели DllGetClassObject, которую функции библиотеки COM используют для создания фабрики класса. Три других экспортируемые функции используются для регистрации компонента.
Функции DllRegisterServer и DllUnregisterServer регистрируют и удаляют из Реестра Windows информацию о компоненте. Мы кратко рассматривали их в гл. 6. Реализованы эти функции в файле REGISTRY.CPP. Я не буду объяснять их код — он достаточно прост, и при желании Вы сможете разобраться сами. Мы будем использовать тот же файл REGISTRY.H для регистрации компонентов и в последующих главах книги.
Make-файл примера этой главы содержит строку
regsvr32 –s Cmpnt.dll