- •Оглавление
- •От автора
- •Введение
- •Преимущества использования компонентов
- •Адаптация приложений
- •Библиотеки компонентов
- •Распределенные компоненты
- •Требования к компонентам
- •Динамическая компоновка
- •Инкапсуляция
- •Заключительные замечания о компонентах
- •Повторное использование архитектур приложений
- •Соглашения о кодировании
- •Законченный пример
- •Взаимодействие в обход интерфейсов
- •Детали реализации
- •Теория интерфейсов, часть 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
203
Поскольку СОМ не выполняет синхронизацию вызовов компонентов, свободным потокам не нужен цикл выборки сообщений. Компонент, созданный свободным потоком, называется компонентом свободных потоков. Такой компонент не принадлежит создавшему его потоку, а используется всеми потоками совместно: все потоки имеют к нему свободный доступ.
Разделенные потоки — единственный тип потоков, которые можно использовать при работе СОМ в Microsoft Windows NT 3.51 и Microsoft Windows 95. В Windows NT 4.0 и в Windows 95 с установленной поддержкой DCOM
можно использовать свободные потоки.
Мы познакомились со свободными потоками в общем. С более интересными подробностями мы столкнемся при обсуждении маршалинга и синхронизации.
Маршалинг и синхронизация
Для правильного маршалинга и синхронизации вызовов компонента СОМ надо знать, потоком какого типа он исполняется. В случае разделенных потоков СОМ обычно выполняет необходимые маршалинг и синхронизацию. Для свободных потоков маршалинг может быть не нужен, а синхронизация возлагается на компонент.
Запомните следующие общие правила:
!" Вызовы между процессами всегда выполняются с использованием маршалинга. Мы обсуждали это в гл.
10.
!" Вызовы внутри одного потока никогда не используют маршалинг. !" Вызов компонента в разделенном потоке выполняется с маршалингом.
!" Вызов компонента в свободном потоке не всегда использует маршалинг. !" Вызовы с помощью разделенного потока синхронизируются.
!" Вызовы с помощью свободного потока не синхронизируются. !" Вызовы внутри потока синхронизируются самим потоком.
Теперь рассмотрим возможные комбинации вызовов разделенных и свободных потоков. Давайте начнем с простых случаев. Если явно не указано иное, подразумевается, что вызовы осуществляются в пределах одного процесса.
Вызовы внутри одного потока
Если клиент, выполняющийся в каком-либо потоке, вызывает компонент, выполняющийся в том же потоке, то вызов синхронизирован просто потому, что поток всего один. СОМ не нужно выполнять какую-либо синхронизацию, и компонент не должен быть «потокобезопасным». Вызовы в пределах одного потока не требуют маршалинга. Это правило мы использовали на протяжении всей книги.
Разделенный — разделенный
Если клиент, выполняющийся в разделенном потоке, вызывает компонент, выполняющийся в другом разделенном потоке, то синхронизацию вызова выполняет СОМ. СОМ также выполняет маршалинг интерфейсов, даже если оба потока находятся в одном процессе. В некоторых случаях требуется выполнить маршалинг интерфейса между разделенными потоками вручную. Мы рассмотрим этот случай позже, когда будем реализовывать разделенный поток. Вызов компонента в разделенном потоке аналогичен вызову компонента вне процесса.
Свободный — свободный
Если клиент, выполняющийся в свободном потоке, вызывает компонент свободных потоков, то СОМ не будет синхронизировать этот вызов. Вызов будет выполнять поток клиента. Компонент должен сам синхронизировать доступ к себе, так как одновременно его может вызвать другой клиент посредством другого потока. Если клиент и компонент находятся внутри одного процесса, маршалинг вызова не выполняется.
Свободный — разделенный
Если клиент в свободном потоке вызывает компонент в подразделении, то синхронизацию вызова осуществляет СОМ. Компонент будет вызван потоком подразделения. Маршалинг интерфейса также необходим, независимо от того, в одном ли процессе или в разных находятся оба потока. В большинстве случаев маршалинг за Вас выполнит СОМ. Но иногда, как Вы скоро увидите, маршалинг приходится выполнять вручную.