Вход/Регистрация
Программирование на Visual C++. Архив рассылки
вернуться

Jenter Алекс

Шрифт:
Ограничения информации о типах в COM и языках программирования

Одно из преимуществ, дарованных нам COM – динамическая загрузка компонентов. Причем загрузка экземпляров конкретных компонентов осуществляется на базе типов. Когда код загружен, программисты разрешают точки входа привязкой объектных ссылок к новым абстрактным типам. Для облегчения первого COM предоставляет CoCreateInstance как типо-ориентированную альтернативу файл-ориентированному вызову API LoadLibrary. Для облегчения последнего COM предоставляет метод QueryInterface как типо-ориентированную альтернативу символьно-ориентированному вызову API GetProcAddress. Посмотрите на следующий COM/C++ код:

IAntique *pAntique = 0;

HRESULT hr = CoCreateInstance(CLSID_Pinto, 0, CLSCTX_ALL, IID_IAntique, (void**)&pAntique);

if (SUCCEEDED(hr)) {

 ICar *pCar = 0;

 hr = pAntique->QueryInterface(IID_ICar, (void**)&pCar);

 if (SUCCEEDED(hr)) {

hr = pCar->AvoidFuelTankCollision;

if (SUCCEEDED(hr)) {

hr = pAntique->Appreciate;

}

pCar->Release;

 }

 pAntique->Release;

}

Заметьте, что нигде нет вызовов LoadLibrary или GetProcAddress. Этот код избавлен от подробностей типа физического размещения DLL– библиотеки (мы даже не знаем, в DLL или в EXE хранится код компонента) или явного запроса адреса метода по символическому имени с последующим преобразованием адреса в указатель на функцию. Но этот код неуклюж и велик по сравнению с кодом создания экземпляра C++-класса и его приведения к базовому классу:

CPinto Antique;

CCar& Car = (CCar)Antique;

Car.AvoidFuelTankCollision;

Antique.Appreciate;

В чем же разница между этими листингами? В первом из них на языке программирования C++ был динамически создан экземпляр компонента, возможно, созданного на другом языке и располагающегося в отдельном исполняемом модуле, а во втором был создан экземпляр класса, определенного в той же программе (а значит, написанного на том же языке, располагающегося в том же модуле…). В остальном же эти листинги идентичны.

Ключ к пониманию недостатков COM спрятан именно в первом листинге. Этот код иллюстрирует напряженность между системой типов COM и системой типов языка реализации (в данном случае, C++). Заметьте, что везде, где объектная ссылка возвращается вызывающей стороне, ее должен сопровождать GUID (в этом примере IID_IAntique или IID_ICar). Это потому, что идентификатор типа языка реализации (std::type_info в случае C++) несовместим с форматом идентификатора типа COM.

За долгие годы группа C++ в Microsoft представила несколько технологий, позволяющих сгладить разницу между системами типов C++ и COM, самой важной (хотя и хитрой) из которых были расширения языка: __uuidof и declspec(uuid). Эти расширения позволили ассоциировать GUID (или, как его еще называют, UUID) с некоторым пользовательским типом. Компилятор MIDL при обработке IDL-файлов автоматически ассоциирует идентификатор типа (GUID) COM с символическим именем C++-типа. При использовании uuidof код становится более типобезопасным:

IAntique *pAntique = 0;

HRESULT hr = CoCreateInstance(__uuidof(Pinto), 0, CLSCTX_ALL, __uuidof(pAntique), (void**)&pAntique);

if (SUCCEEDED(hr)) {

 ICar *pCar = 0;

 hr = pAntique->QueryInterface(__uuidof(pCar), (void**)&pCar);

 if (SUCCEEDED(hr)) {

hr = pCar->AvoidFuelTankCollision;

if (SUCCEEDED(hr)) hr = pAntique->Appreciate;

pCar->Release;

 }

 pAntique->Release;

}

Заметьте, что, если понадобится изменить тип pAntique, в первом листинге придется менять и IID, а во втором все произойдет автоматически, так как оператор __uuidof всегда получает нужный IID отталкиваясь от pAntique.

Более того, если воспользоваться возможностями самого C++, можно создать универсальные классы-обертки, еще более упрощающие жизнь программиста. Так при использовании CComPtr или поддержки COM компилятором (compiler COM support) можно написать примерно такой код:

CComPtr<IAntique> spIAntique;

HRESULT hr = spIAntique.CoCreateInstance(__uuidof(Pinto));

if (SUCCEEDED(hr)) {

 CComQIPtr<ICar> spICar(spIAntique);

 if (spIUnknown) hr = spICar->AvoidFuelTankCollision;

 if (SUCCEEDED(hr)) hr = spIAntique->Appreciate;

}

Хотя этот код, несомненно, проще и безопаснее, он все же далек от идеала, причем как с точки зрения простоты и читабельности, так и с точки зрения типобезопасности. Ведь только во время исполнения программы будет точно известно, реализует объект эти интерфейсы или нет. Несмотря на использование __uuidof, чтобы заставить COM работать с C++, нужно отключить систему проверки типов C++ на время трансляции объектных ссылок COM в C++-типы. В отличие от этого, интеграция COM с виртуальной машиной Java Microsoft позволяет написать следующее:

  • Читать дальше
  • 1
  • ...
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: