- •Оглавление
- •От автора
- •Введение
- •Преимущества использования компонентов
- •Адаптация приложений
- •Библиотеки компонентов
- •Распределенные компоненты
- •Требования к компонентам
- •Динамическая компоновка
- •Инкапсуляция
- •Заключительные замечания о компонентах
- •Повторное использование архитектур приложений
- •Соглашения о кодировании
- •Законченный пример
- •Взаимодействие в обход интерфейсов
- •Детали реализации
- •Теория интерфейсов, часть 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
40
Из рис. 3-3 видно, что указатель this для CA указывает на указатель таблицы виртуальных функций IX. Таким образом, мы можем без изменения использовать для CA указатель this вместо указателя на IX. Однако очевидно, что указатель this для CA не указывает на указатель vtbl IY. Следовательно, указатель this CA надо модифицировать, прежде чем передавать функции, ожидающей указатель на IY. Для этого компилятор добавляет к указателю this CA смещение указателя vtbl IY (∆ IY). Приведенный ниже код:
IY* pC = pA;
компилятор транслирует во что-то вроде
IY* pC = (char*)pA + ∆ IY;
Более подробную информацию Вы можете найти в разделе «Multiple Inheritance and Casting» книги Маргарет А. Эллис (Margaret A. Ellis) и Бьерна Страуструпа (Bjarne Strourstrup) The Annotated C++ Reference Manual.
Компиляторы С++ не обязаны реализовывать vtbl при множественном наследовании именно так, как это показано на рис. 3-3.
А теперь все вместе
Давайте соберем вместе все элементы и рассмотрим полный пример реализации и использования QueryInterface.
Влистинге 3-1 представлен полный текст этой простой программы. Копию программы можно найти на прилагаемом к книге диске. Программа состоит из трех частей.
Впервой части объявляются интерфейсы IX, IY и IZ. Интерфейс IUnknown объявлен в заголовочном файле
UNKNWN.H Win32 SDK.
Вторая часть — это реализация компонента. Класс CA реализует компонент, поддерживающий интерфейсы IX и IY. Реализация QueryInterface совпадает с той, что была представлена в предыдущем разделе книги. Функция CreateInstance определена после класса CA. Клиент использует ее, чтобы создать компонент, предоставляемый при помощи CA, и получить указатель на IUnknown этого компонента.
После CreateInstance следуют определения IID для интерфейсов. Как видно их этих определений, IID — весьма громоздкая структура (более подробно мы рассмотрим ее в гл. 7). Наш пример программы компонуется с UUID.LIB, чтобы получить определения для IID_IUnknown (т.е. IID для IUnknown).
Третья и последняя часть — функция main, которая выступает в качестве клиента.
IUNKNOWN.CPP
//
//IUnknown.cpp
//Чтобы скомпилировать: cl IUnknown.cpp UUID.lib
//
#include <iostream.h> #include <objbase.h>
void trace(const char* msg) { cout << msg << endl; }
// Интерфейсы
interface IX : IUnknown
{
virtual void __stdcall Fx() = 0; };
interface IY : IUnknown
{
virtual void __stdcall Fy() = 0; };
interface IZ : IUnknown
{
virtual void __stdcall Fz() = 0; };
// Предварительные объявления GUID extern const IID IID_IX;
extern const IID IID_IY; extern const IID IID_IZ;
41
//
// Компонент
//
class CA : public IX, public IY
{
// Реализация IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv); virtual ULONG __stdcall AddRef() { return 0; }
virtual ULONG __stdcall Release() { return 0; }
// Реализация интерфейса IX
virtual void __stdcall Fx() { cout << "Fx" << endl; }
// Реализация интерфейса IY
virtual void __stdcall Fy() { cout << "Fy" << endl; }
};
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown)
{
trace("QueryInterface: Вернуть указатель на IUnknown"); *ppv = static_cast<IX*>(this);
}
else if (iid == IID_IX)
{
trace("QueryInterface: Вернуть указатель на IX"); *ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY)
{
trace("QueryInterface: Вернуть указатель на IY"); *ppv = static_cast<IY*>(this);
}
else
{
trace("QueryInterface: Интерфейс не поддерживается");
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef(); // См. гл. 4 return S_OK;
}
//
// Функция создания
//
IUnknown* CreateInstance()
{
IUnknown* pI = static_cast<IX*>(new CA); pI->AddRef();
return pI;
}
//
//IID
//{32bb8320-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IX =
{0x32bb8320, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
//{32bb8321-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IY = {0x32bb8321, 0xb41b, 0x11cf,
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
// {32bb8322-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IZ =
{0x32bb8322, 0xb41b, 0x11cf,
42
{0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}};
//
// Клиент
//
int main()
{
HRESULT hr;
trace("Клиент: Получить указатель на IUnknown"); IUnknown* pIUnknown = CreateInstance();
trace("Клиент: Получить указатель на IX");
IX* pIX = NULL;
hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX); if (SUCCEEDED(hr))
{
trace("Клиент: IX получен успешно");
pIX->Fx(); // Использовать интерфейс IX
}
trace("Клиент: Получить указатель на IY");
IY* pIY = NULL;
hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY); if (SUCCEEDED(hr))
{
trace("Клиент: IY получен успешно");
pIY->Fy(); |
// Использовать интерфейс IY |
} |
|
trace("Клиент: Запросить неподдерживаемый интерфейс");
IZ* pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ); if (SUCCEEDED(hr))
{
trace("Клиент: Интерфейс IZ получен успешно"); pIZ->Fz();
}
else
{
trace("Клиент: Не могу получить интерфейс IZ");
}
trace("Клиент: Получить интерфейс IY через интерфейс IX");
IY* pIYfromIX = NULL;
hr = pIX->QueryInterface(IID_IY, (void**)&pIYfromIX); if (SUCCEEDED(hr))
{
trace("Клиент: IY получен успешно"); pIYfromIX->Fy();
}
trace("Клиент: Получить интерфейс IUnknown через IY");
IUnknown* pIUnknownFromIY = NULL;
hr = pIY->QueryInterface(IID_IUnknown, (void**)&pIUnknownFromIY); if (SUCCEEDED(hr))
{
cout << "Совпадают ли указатели на IUnknown? "; if (pIUnknownFromIY == pIUnknown)
{
cout << "Да, pIUnknownFromIY == pIUnknown" << endl;
}
else
{
cout << "Нет, pIUnknownFromIY != pIUnknown" << endl;