- •Оглавление
- •От автора
- •Введение
- •Преимущества использования компонентов
- •Адаптация приложений
- •Библиотеки компонентов
- •Распределенные компоненты
- •Требования к компонентам
- •Динамическая компоновка
- •Инкапсуляция
- •Заключительные замечания о компонентах
- •Повторное использование архитектур приложений
- •Соглашения о кодировании
- •Законченный пример
- •Взаимодействие в обход интерфейсов
- •Детали реализации
- •Теория интерфейсов, часть 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
79
double GetCordLength(double BladeSection);
Вместо этого возвращайте из функции HRESULT, а все результаты передавайте через выходные параметры:
HRESULT GetCordLength(/* in */ double BladeSection, /* out */ double* pLength);
HRESULT передает клиенту информацию, необходимую для обнаружения сетевых ошибок. Вызовы функций в Автоматизации (ранее OLE Автоматизация) удовлетворяют этому требованию. Более подробно удаленные компоненты будут рассмотрены в гл. 10.
Определение собственных кодов ошибки
СОМ определяет универсальные коды возврата, таки как S_OK и E_UNEXPECTED. Разработчики интерфейсов ответственны за коды возврата, специфичные для их интерфейсов. HRESULT, содержащий специфичный для интерфейса код возврата, должен также содержать идентификатор средства FACILITY_ITF. Он указывает клиенту, что код специфичен для данного интерфейса.
Хотя смысл кода возврата, отмеченного с помощью FACILITY_ITF, специфичен для возвращающего его интерфейса, само по себе соответствующее число не уникально — возможны только 216 разных значений. Тысячи разработчиков пишут свои компоненты СОМ со своими кодами возврата. Все такие коды помечены с помощью FACILITY_ITF. Поэтому не просто с очень большой вероятностью, но и с гарантией разные интерфейсы придадут разный смысл одним и тем же кодам возврата. Тридцати двух разрядов недостаточно, чтобы дать каждому разработчику ввести свой собственный идентификатор средства, а большая длина HRESULT снизила бы эффективность. В качестве кодов возврата GUID не являются разумной альтернативой длинным целым значениям, поскольку размер GUID слишком велик. Однако, поскольку FACILITY_ITF отмечает каждый код возврата как специфичный для интерфейса, постольку такой код возврата связан с идентификатором интерфейса
(IID).
Для клиента, вызывающего функции интерфейса, возможность конфликта кодов возврата — не проблема. Клиент знает, к кому он обращается, и, таким образом, знает все коды успеха данного интерфейса. Клиент также известна большая часть кодов ошибок. Клиент должен рассматривать любой неизвестный ему код ошибки как E_UNEXPECTED. Однако проблемы начинаются, когда клиент интерфейса сам является компонентом, который пытается без изменения передать возвращаемый код успеха или ошибки своему клиенту. Последний не поймет код, поскольку не знает, к какому первоначальному интерфейсу тот относится.
Например, предположим, что первый клиент вызывает функцию IX::Fx, которая затем вызывает IY::Fy. Если IY::Fy возвращает HRESULT с FACILITY_ITF, то IX::Fx не может передать этот код первому клиенту. Данный клиент знает только о IX — и будет полагать, что HRESULT относится к IX, а не IY. Следовательно, IX::Fx должна транслировать возвращаемые IY значения HRESULT с FACILITY_ITF в такие значения, которые понятны первому клиенту. Для неизвестных ошибок у IX нет иного выбора, кроме как возвращать E_UNEXPECTED. Для кодов успешного завершения IX должен возвращать свои собственные, документированные коды возврата.
Вот некоторые основные правила определения собственных HRESULT:
!" Не назначайте кодам возврата значения из диапазона 0x0000 — 0x01FF. Они зарезервированы для кодов FACILITY_ITF, определенных СОМ.
!" Не возвращайте клиенту коды с признаком FACILITY_ITF без изменения.
!" Используйте универсальные коды успеха и ошибки СОМ всегда, когда только возможно.
!" Избегайте определения собственных HRESULT; вместо этого используйте выходные параметры Вашей функции.
Теперь, когда Вы получили некоторое представление о HRESULT, создадим полный код при помощи макроса MAKE_HRESULT. По заданному признаку критичности, идентификатору средства и коду завершения MAKE_HRESULT создает HRESULT. Вот два примера:
MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 512);
MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_ITF, 513);
По соглашению нестандартным кодам завершения дается в качестве префикса имя компонента или интерфейса. Например, двум приведенным выше кодам можно было бы дать имена
AIRPLANE_E_LANDINGWITHGEARUP
HELICOPTER_S_ROTORRPMGREEN
Сказанного о HRESULT более чем достаточно. Теперь пора снять завесу таинственности с GUID.