- •Оглавление
- •От автора
- •Введение
- •Преимущества использования компонентов
- •Адаптация приложений
- •Библиотеки компонентов
- •Распределенные компоненты
- •Требования к компонентам
- •Динамическая компоновка
- •Инкапсуляция
- •Заключительные замечания о компонентах
- •Повторное использование архитектур приложений
- •Соглашения о кодировании
- •Законченный пример
- •Взаимодействие в обход интерфейсов
- •Детали реализации
- •Теория интерфейсов, часть 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
121
Большая часть этого кода может показаться избыточной и ненужной. Но если внешний компонент сам агрегирован другим компонентом, выполнение описанных выше шагов становится критически важным. В примере из гл. 9 показан компонент, который агрегирует компонент, агрегирующий другой компонент.
«Вот и все, что необходимо сделать для реализации агрегирования», — сказал он, улыбаясь. В действительности, после того, как Вы написали корректный код агрегирования, он работает отлично, и о нем можно забыть. Однако после первой попытки его написать многие начинают называть его не «aggregation», а «aggravation»*.
Законченный пример
Реализуем компонент, который агрегирует некий интерфейс. В данном примере Компонент 1 поддерживает два интерфейса, так же как и в примере с включением. Однако здесь он реализует только IX. Он не будет ни реализовывать IY, ни передавать его вызовы реализации этого интерфейса Компонентом 2. Вместо этого, когда клиент запрашивает у Компонента 1 интерфейс IY, Компонент 1 возвращает указатель на интерфейс IY, реализуемый внутренним Компонентом 2. В листинге 8-3 представлен внешний компонент, а в листинге 8-4 — внутренний. Клиент остался практически неизменным; ему совершенно неважно, используем ли мы агрегирование или включение.
AGGRGATE\CMPNT1
//
// Cmpnt1.cpp - Компонент 1
//
// Интересные части кода выделены полужирным шрифтом
//
#include <iostream.h> #include <objbase.h>
#include "Iface.h" #include "Registry.h"
// Функция Trace
void trace(const char* msg) { cout << "Компонент 1:\t" << msg << endl; }
///////////////////////////////////////////////////////////
//
//Глобальные переменные
//Статические переменные
static HMODULE g_hModule = |
NULL; |
// Дескриптор модуля DLL |
||||
static |
long |
g_cComponents = 0; |
// |
Счетчик активных |
компонентов |
|
static |
long |
g_cServerLocks |
= 0; |
// |
Число блокировок |
|
// Дружественное имя компонента
const char g_szFriendlyName[] = "Основы COM, Глава 8 Пример 2, Компонент 1";
// Не зависящий от версии ProgID
const char g_szVerIndProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt1";
// ProgID
const char g_szProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt1.1";
///////////////////////////////////////////////////////////
//
// Компонент A
//
class CA : public IX //, public IY
{
public:
// IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
virtual ULONG |
__stdcall AddRef(); |
virtual ULONG |
__stdcall Release(); |
// Интерфейс IX |
|
* Aggravation (англ.) — 1. Ухудшение, усугубление; 2. Раздражение, огорчение. — Прим. перев.
122
virtual void __stdcall Fx() { cout << "Fx" << endl; }
/* Компонент 1 агрегирует интерфейс IY, а не реализует его
// Интерфейс IY
virtual void __stdcall Fy() { m_pIY->Fy(); }
*/
//Конструктор
CA();
//Деструктор
~CA();
//Функция инициализации, вызываемая фабрикой класса для
//создания агрегируемого компонента
HRESULT __stdcall Init();
private:
//Счетчик ссылок long m_cRef;
//Указатель на интерфейс IY агрегированного компонента
//(Нам необязательно сохранять указатель на IY. Однако мы
//можем использовать его в QueryInterface)
IY* m_pIY;
// Указатель на IUnknown внутреннего компонента
IUnknown* m_pUnknownInner;
};
//
// Конструктор
//
CA::CA() : m_cRef(1), m_pUnknownInner(NULL)
{
::InterlockedIncrement(&g_cComponents);
}
//
// Деструктор
//
CA::~CA()
{
::InterlockedDecrement(&g_cComponents); trace("Самоликвидация");
//Предотвращение рекурсивного вызова деструктора следующей
//ниже пары AddRef/Release
m_cRef = 1;
//Учесть pUnknownOuter->Release в методе Init IUnknown* pUnknownOuter = this; pUnknownOuter->AddRef();
//Правильное освобождение указателя; возможен поинтерфейсный
//подсчет ссылок
m_pIY->Release();
// Освободить внутренний компонент if (m_pUnknownInner != NULL)
{
m_pUnknownInner->Release();
}
}
// Инициализировать компонент путем создания внутреннего компонента
HRESULT __stdcall CA::Init()
{
// Получить указатель на внешний IUnknown
123
//Поскольку этот компонент агрегируется, внешний IUnknown -
//это то же самое, что и указатель this
IUnknown* pUnknownOuter = this;
trace("Создать внутренний компонент");
HRESULT hr = ::CoCreateInstance(CLSID_Component2,
pUnknownOuter, |
// IUnknown внешнего компонента |
CLSCTX_INPROC_SERVER, |
|
IID_IUnknown, |
// При агрегировании - IUnknown |
(void**)&m_pUnknownInner);
if (FAILED(hr))
{
trace("Не могу создать внутренний компонент"); return E_FAIL;
}
// Следующий вызов будет увеличивать счетчик ссылок внешнего компонента trace("Получить интерфейс IY внутреннего компонента");
hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY); if (FAILED(hr))
{
trace("Внутренний компонент не поддерживает интерфейс IY"); m_pUnknownInner->Release();
return E_FAIL;
}
//Необходимо уменьшить счетчик ссылок внешнего компонента, увеличенный
//предыдущим вызовом. Для этого вызываем Release для указателя,
//переданного CoCreateInstance.
pUnknownOuter->Release(); return S_OK;
}
//
// Реализация IUnknown
//
HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown)
{
*ppv = static_cast<IUnknown*>(this);
}
else if (iid == IID_IX)
{
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY)
{
trace("Вернуть интерфейс IY внутреннего компонента");
#if 1
// Этот интерфейс можно запросить...
return m_pUnknownInner->QueryInterface(iid,ppv);
#else
//либо можно вернуть сохраненный указатель
*ppv = m_pIY;
//Проходим дальше, чтобы была вызвана AddRef
#endif
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK;
}
ULONG __stdcall CA::AddRef()
{
return ::InterlockedIncrement(&m_cRef);
124
}
ULONG __stdcall CA::Release()
{
if (::InterlockedDecrement(&m_cRef) == 0)
{
delete this; return 0;
}
return m_cRef;
}
///////////////////////////////////////////////////////////
//
// Фабрика класса
//
class CFactory : public IClassFactory
{ |
|
public: |
|
// IUnknown |
|
virtual HRESULT |
__stdcall QueryInterface(const IID& iid, void** ppv); |
virtual ULONG |
__stdcall AddRef(); |
virtual ULONG |
__stdcall Release(); |
// Интерфейс IClassFactory
virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid,
void** ppv); virtual HRESULT __stdcall LockServer(BOOL bLock);
// Конструктор
CFactory() : m_cRef(1) {}
// Деструктор
~CFactory() {}
private:
long m_cRef; };
//
// Реализация IUnknown для фабрики класса
//
HRESULT __stdcall CFactory::QueryInterface(REFIID iid, void** ppv)
{
IUnknown* pI;
if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
{
pI = static_cast<IClassFactory*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
pI->AddRef(); *ppv = pI; return S_OK;
}
ULONG __stdcall CFactory::AddRef()
{
return ::InterlockedIncrement(&m_cRef);
}
ULONG __stdcall CFactory::Release()
{
if (::InterlockedDecrement(&m_cRef) == 0)
{
delete this;
125
return 0;
}
return m_cRef;
}
//
// Реализация IClassFactory
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid,
void** ppv)
{
//Агрегирование не поддерживается if (pUnknownOuter != NULL)
{
return CLASS_E_NOAGGREGATION;
}
//Создать компонент
CA* pA = new CA; if (pA == NULL)
{
return E_OUTOFMEMORY;
}
// Инициализировать компонент
HRESULT hr = pA->Init(); if (FAILED(hr))
{
// Ошибка инициализации. Удалить компонент. pA->Release();
return hr;
}
// Получить запрошенный интерфейс hr = pA->QueryInterface(iid, ppv); pA->Release();
return hr;
}
// LockServer
HRESULT __stdcall CFactory::LockServer(BOOL bLock)
{
if (bLock)
{
::InterlockedIncrement(&g_cServerLocks);
}
else
{
::InterlockedDecrement(&g_cServerLocks);
}
return S_OK;
}
///////////////////////////////////////////////////////////
//
// Экспортируемые функции
//
STDAPI DllCanUnloadNow()
{
if ((g_cComponents == 0) && (g_cServerLocks == 0))
{
return S_OK;
}
else
{
return S_FALSE;
}
}
126
//
// Получение фабрики класса
//
STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)
{
//Можем ли мы создать такой компонент? if (clsid != CLSID_Component1)
{
return CLASS_E_CLASSNOTAVAILABLE;
}
//Создать фабрику класса
CFactory* pFactory = new CFactory; |
// В конструкторе нет Addref |
if (pFactory == NULL) |
|
{ |
|
return E_OUTOFMEMORY; |
|
} |
|
// Получить запрошенный интерфейс
HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release();
return hr;
}
//
// Регистрация сервера
//
STDAPI DllRegisterServer()
{
return RegisterServer(g_hModule, CLSID_Component1, g_szFriendlyName, g_szVerIndProgID, g_szProgID);
}
STDAPI DllUnregisterServer()
{
return UnregisterServer(CLSID_Component1, g_szVerIndProgID, g_szProgID);
}
///////////////////////////////////////////////////////////
//
// Информация о модуле DLL
//
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
g_hModule = hModule;
}
return TRUE;
}
Листинг 8-3 Реализация внешнего (агрегирующего) компонента
AGGRGATE\CMPNT2
//
//Cmpnt2.cpp - Компонент 2
//Помните, что изменения в фабрике класса отмечены полужирным шрифтом
//
#include <iostream.h>
127
#include <objbase.h>
#include "Iface.h" #include "Registry.h"
void trace(const char* msg) { cout << "Компонент 2:\t" << msg << endl; }
///////////////////////////////////////////////////////////
//
//Глобальные переменные
//Статические переменные
static HMODULE g_hModule = |
NULL; |
// Дескриптор |
модуля DLL |
||
static |
long |
g_cComponents = 0; |
// Счетчик активных компонентов |
||
static |
long |
g_cServerLocks |
= 0; |
// Количество |
блокировок |
// Дружественное имя компонента const char g_szFriendlyName[]
="Основы COM, Глава 8 Пример 2, Компонент 2";
//Независящий от версии ProgID
const char g_szVerIndProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt2";
// ProgID
const char g_szProgID[] = "InsideCOM.Chap08.Ex2.Cmpnt2.1";
///////////////////////////////////////////////////////////
//
// Неделегирующий интерфейс IUnknown
//
struct INondelegatingUnknown
{
virtual HRESULT __stdcall NondelegatingQueryInterface(const IID&, void**) = 0;
virtual ULONG __stdcall NondelegatingAddRef() = 0; virtual ULONG __stdcall NondelegatingRelease() = 0;
};
///////////////////////////////////////////////////////////
//
// Компонент B
//
class CB : public IY, public INondelegatingUnknown
{
public:
// Делегирующий IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv)
{
trace("Делегирующий QueryInterface");
return m_pUnknownOuter->QueryInterface(iid, ppv);
}
virtual ULONG __stdcall AddRef()
{
trace("Делегировать AddRef"); return m_pUnknownOuter->AddRef();
}
virtual ULONG __stdcall Release()
{
trace("Делегировать Release"); return m_pUnknownOuter->Release();
}
// Неделегирующий IUnknown virtual HRESULT __stdcall
NondelegatingQueryInterface(const IID& iid, void** ppv);
virtual |
ULONG |
__stdcall |
NondelegatingAddRef(); |
virtual |
ULONG |
__stdcall |
NondelegatingRelease(); |
128
// Интерфейс IY
virtual void __stdcall Fy() { cout << "Fy" << endl; }
// Конструктор
CB(IUnknown* m_pUnknownOuter);
// Деструктор
~CB();
private:
long m_cRef;
IUnknown* m_pUnknownOuter; };
//
// Реализация IUnknown
//
HRESULT __stdcall CB::NondelegatingQueryInterface(const IID& iid, void** ppv)
{
if (iid == IID_IUnknown)
{
// !!! ПРИВЕДЕНИЕ ТИПА ОЧЕНЬ ВАЖНО !!!
*ppv = static_cast<INondelegatingUnknown*>(this);
}
else if (iid == IID_IY)
{
*ppv = static_cast<IY*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK;
}
ULONG __stdcall CB::NondelegatingAddRef()
{
return ::InterlockedIncrement(&m_cRef);
}
ULONG __stdcall CB::NondelegatingRelease()
{
if (::InterlockedDecrement(&m_cRef) == 0)
{
delete this; return 0;
}
return m_cRef;
}
//
// Конструктор
//
CB::CB(IUnknown* pUnknownOuter) : m_cRef(1)
{
::InterlockedIncrement(&g_cComponents);
if (pUnknownOuter == NULL)
{
trace("Не агрегируется; использовать неделегирующий IUnknown"); m_pUnknownOuter = reinterpret_cast<IUnknown*>
(static_cast<INondelegatingUnknown*> (this));
}
else
{
129
trace("Агрегируется; делегировать внешнему IUnknown"); m_pUnknownOuter = pUnknownOuter;
}
}
//
// Деструктор
//
CB::~CB()
{
::InterlockedDecrement(&g_cComponents); trace("Саморазрушение");
}
///////////////////////////////////////////////////////////
//
// Фабрика класса
//
class CFactory : public IClassFactory
{
public:
// IUnknown
virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
virtual |
ULONG |
__stdcall |
AddRef(); |
virtual |
ULONG |
__stdcall |
Release(); |
// Интерфейс IClassFactory
virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid,
void** ppv); virtual HRESULT __stdcall LockServer(BOOL bLock);
// Конструктор
CFactory() : m_cRef(1) {}
// Деструктор
~CFactory() {}
private:
long m_cRef; };
//
// Реализация IUnknown для фабрики класса
//
HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
{
if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
{
*ppv = static_cast<IClassFactory*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK;
}
ULONG __stdcall CFactory::AddRef()
{
return ::InterlockedIncrement(&m_cRef);
}
ULONG __stdcall CFactory::Release()
{
if (::InterlockedDecrement(&m_cRef) == 0)
{
delete this;
130
return 0;
}
return m_cRef;
}
//
// Реализация IClassFactory
//
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid,
void** ppv)
{
// При агрегировании iid должен быть IID_IUnknown
if ((pUnknownOuter != NULL) && (iid != IID_IUnknown))
{
return CLASS_E_NOAGGREGATION;
}
// Создать компонент
CB* pB = new CB(pUnknownOuter); if (pB == NULL)
{
return E_OUTOFMEMORY;
}
// Получить запрошенный интерфейс
HRESULT hr = pB->NondelegatingQueryInterface(iid, ppv); pB->NondelegatingRelease();
return hr;
}
// LockServer
HRESULT __stdcall CFactory::LockServer(BOOL bLock)
{
if (bLock)
{
::InterlockedIncrement(&g_cServerLocks);
}
else
{
::InterlockedDecrement(&g_cServerLocks);
}
return S_OK;
}
///////////////////////////////////////////////////////////
//
// Экспортируемые функции
//
STDAPI DllCanUnloadNow()
{
if ((g_cComponents == 0) && (g_cServerLocks == 0))
{
return S_OK;
}
else
{
return S_FALSE;
}
}
//
// Получение фабрики класса
//
STDAPI DllGetClassObject(const CLSID& clsid, const IID& iid, void** ppv)
{
// Можем ли мы создать такой компонент? if (clsid != CLSID_Component2)
{