Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
Шрифт:
Интервал:
Закладка:
pcr->RegisterCategories(4, rgcc);
// note that Chimps are Simians and mammals
// отметим, что Chimps (шимпанзе) являются Simian
// (обезьянами) и Mammal (млекопитающими)
CATID rgcid[2];
rgcid[0] = CATID_Simian;
rgcid[1] = CATID_Mammal;
pcr->RegisterClassImplCategories(CLSID_Chimp, 2, rgcid);
// note that Chimps require Oxygen and Water
// отметим, что Chimps (шимпанзе) нуждаются
// в кислороде (Oxygen) и воде (Water)
rgcid[0] = CATID_HasOxygen;
rgcid[1] = CATID_HasWater;
pcr->RegisterClassReqCategories(CLSID_Chimp, 2, rgcid);
pcr->Release(); }
Заметим, что в этом коде не делается обычных вызовов реестровых API-функций, а вместо них для обработки реестра используется стандартный менеджер категорий.
Кроме того, стандартный менеджер категорий позволяет приложениям запрашивать реестр найти информацию о категориях. Эта функциональная возможность предоставляется через интерфейс ICatInformation:
[object, uuid(0002E013-0000-0000-C000-000000000046)]
interface ICatInformation : IUnknown
{
// get list of known categories
// получаем список известных категорий
HRESULT EnumCategories([in] LCID lcid, [out] IEnumCATEGORYINFO** ppeci);
// get description of a particular category
// получаем описание определенной категории
HRESULT GetCategoryDesc([in] REFCATID rcatid, [in] LCID lcid, [out] OLECHAR ** ppszDesc);
// get list of classes compatible with specified categories
// получаем список классов, совместимых с указанными категориями
HRESULT EnumClassesOfCategories(
[in] ULONG cImplemented,
// -1 indicates ignore
// (-1) означает игнорировать
[in,size_is(cImplemented)] CATID rgcatidImpl[], [in] ULONG cRequired,
// -1 indicates ignore
// (-1) означает игнорировать
[in,size_is(cRequired)] CATID rgcatidReq[], [out] IEnumCLSID** ppenumClsid);
// verify class is compatible with specified categories
// проверяем, совместим ли класс с указанными категориями
HRESULT IsClassOfCategories([in] REFCLSID rclsid,
[in] ULONG cImplemented,
[in,size_is(cImplemented)] CATID rgcatidImpl[],
[in] ULONG cRequired,
[in,size_is(cRequired)] CATID rgcatidReq[]);
// get list of class's implemented categories
// получаем список реализованных категорий класса
HRESULT EnumImplCategoriesOfClass([in] REFCLSID rclsid,
[out] IEnumCATID** ppenumCatid);
// get list of class's required categories
// получаем список категорий, необходимых классу
HRESULT EnumReqCategoriesOfClass([in] REFCLSID rclsid,
[out] IEnumCATID** ppenumCatid);
}
Большинство этих методов возвращают свои курсоры на списки идентификаторов категории или класса. Эти указатели называются нумераторами (enumerators ) и подробно описываются в главе 7.
Следующий код показывает, как выделить список классов, являющихся членами категории Mammal:
// get the standard category manager // получаем стандартный менеджер категорий
ICatInformation *pci = 0; HRESULT hr = CoCreateInstance(
CLSID_StdComponentCategoriesMgr, 0,
CLSCTX_ALL, IID_ICatInformat1on, (void**)&pci); if (SUCCEEDED(hr)) {
// get the classes that are Simians (ignore required cats)
// получаем классы, являющиеся Simian
// (игнорируем требуемые категории)
IEnumCLSID *pec = 0;
CATID rgcid[1];
rgcid[0] = CATID_Simian;
hr = pci->EnumClassesOfCategories(1, rgcid, -1, 0, &pec);
if (SUCCEEDED(hr)) {
// walk list of CLSIDs 64 at a time
// просматриваем список CLSID no 64 за проход
enum { MAX = 64 };
CLSID rgclsid[MAX];
do {
ULONG cActual = 0;
hr = pec->Next(MAX, rgclsid, &cActual);
if (SUCCEEDED(hr)) {
for (ULONG i = 0; i < cActual; i++)
DisplayClass(rgclsid[i]);
}
}
while (hr == S_OK);
pec->Release();
}
pci->Release(); }
Этот фрагмент кода игнорирует то обстоятельство, что клиентская программа может не поддерживать нужные категории результирующего списка классов. Если бы клиент был осведомлен о том, какие локальные категории им поддерживаются, то он мог бы указать список всех поддерживаемых категорий.
Рассмотрим следующий вызов EnumClassesOfCategories:
CATID rgimpl[1]; rgimpl[0] = CATID_Simians;
CATID rgreq[3]; rgreq[0] = CATID_HasWater;
rgreq[1] = CATID_HasOxygen; rgreq[2] = CATID_HasMilk;
hr =pci->EnumClassesOfCategories(1, rgimpl, 3, rgreq, &pec);
Результирующий список классов будет содержать всех приматов (Simians), которые не требуют от среды клиента ничего, кроме кислорода (Oxygen), воды (Water) и молока (Milk). Класс Chimp, зарегистрированный ранее, мог бы быть совместимым классом, так как он реализует специфицированную категорию Simian и требует подмножество специфицированных категорий, использованных в запросе.
Заключительным, причем спорным, аспектом категорий компонентов является представление о классе по умолчанию для категории. СОМ допускает регистрацию CATID в качестве CLSID под ключом реестра HKEY_CLASSES_ROOTCLSID
Для преобразования CATID в CLSID по умолчанию используется средство TreatAs , введенное эмуляцией. Для указания того, что класс Gorilla является классом по умолчанию для Simian, необходимо добавить следующий ключ реестра:
[HKCRCLSID{CATID_Simian}TreatAs] @={CLSID_Gorilla}
Это простое соглашение позволяет клиентам просто использовать CATID там, где ожидаются CLSID:
// create an instance of the default Simian class
// создаем экземпляр класса Simian, принятого по умолчанию
hr = CoCreateInstance(CATID_Simian, 0, CLSCTX_ALL, IID_IApe, (void**)&pApe);
Если для указанной категории не зарегистрировано ни одного класса по умолчанию, то вызов активации даст сбой и вернет REGDB_E_CLASSNOTREG.
Где мы находимся?
В этой главе представлена концепция СОМ-класса. СОМ-классами называются конкретные типы данных, которые экспортируют один или более интерфейсов и являются основной абстракцией, используемой при активации объектов в СОМ. СОМ поддерживает три примитива активации. CoGetClassObject связывает ссылку с объектом класса, который представляет независимые от экземпляра функциональные возможности класса. CoCreateInstanceEx связывает ссылку с новым экземпляром класса, a CoGetInstanceFromFile связывает ссылку с постоянным экземпляром, находящимся в файле. Моникеры используются в качестве универсальной абстракции для передачи клиентам стратегии связывания и активации, причем MkParseDisplayName выполняет функции точки входа в пространство имен СОМ.
Глава 4. Объекты
class object
{
public:
template <class T> virtual T * dynamic_cast(const type_info& t = typeid(T) )
};
Аноним, 1995В главе 2 обсуждались основы интерфейсов СОМ вообще и интерфейс IUnknown в частности. Было дано понятие о том, что путем наследования дополнительным интерфейсам объекты могут выставлять более одного вида функциональных возможностей. Был также продемонстрирован механизм, с помощью которого клиенты могут опрашивать объекты, чтобы найти среди них доступные функциональные возможности. Этот механизм – QueryInterface (Интерфейс запросов) – был выделен как версия С++-оператора преобразования типа dynamic_cast, не зависящая от языка программирования и от компилятора.
В предыдущей главе было показано, что QueryInterface можно реализовать непосредственно, используя статические преобразования типа для того, чтобы ограничить область действия указателя this на объект типом интерфейса, который запрашивается клиентом. На физическом уровне этот способ означает просто преобразование идентификаторов интерфейса в объект с помощью соответствующего смещения, то есть способ, который применяется любым компилятором C++ при реализации dynamic_cast.
Хотя реализации QueryInterface из предыдущей главы являются вполне допустимыми для СОМ, правила IUnknown предоставляют разработчику объектов значительно больше гибкости, чем было показано до сих пор. В данной главе эти правила будут исследованы, и продемонстрированы способы реализации, которые из них вытекают.
Снова IUnknown
IUnknown не имеет реализации по умолчанию, которая являлась бы частью интерфейса системного вызова СОМ. Заголовочные файлы SDK не содержат базовых классов, макросов или шаблонов, предусматривающих реализации QueryInterface, AddRef и Release, которые должны использоваться во всех программах на С или C++. Вместо этого Спецификация СОМ (Component Object Model Specification) предоставляет очень точные правила относительно допущений, которые клиенты и объекты могут делать относительно этих трех методов. Этот набор правил формирует протокол IUnknown и позволяет каждому разработчику объекта преобразовать три указанных метода IUnknown во все, что имеет смысл для его или ее объекта.
В главе 2 представлены фактические С++-реализации трех упомянутых методов, но СОМ никоим образом не обязывает объекты использовать их. Все, что требует СОМ, – это чтобы каждая реализация придерживалась базовых правил IUnknown. Как это достигается, не имеет ни малейшего отношения к СОМ. Это делает СОМ совершенно ненавязчивой, так как эта модель не требует, чтобы объект делал системные вызовы, наследовал системным реализациям, а все, что от него требуется, – это объявлять совместимые с СОМ указатели vptr. На самом деле, как будет показано далее в этой главе, можно выставлять наследующие IUnknown указатели vptr из классов, которые не наследуют ни одному интерфейсу СОМ.
Правила IUnknown в совокупности определяют, что значит быть объектом СОМ. Чтобы понять правила IUnknown, полезно начать с конкретного примера. Рассмотрим следующую иерархию интерфейсов: