Алекс Jenter - Программирование на Visual C++. Архив рассылки
- Название:Программирование на Visual C++. Архив рассылки
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Алекс Jenter - Программирование на Visual C++. Архив рассылки краткое содержание
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.
Программирование на Visual C++. Архив рассылки - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
MSIL – это некоторый язык инструкций, похожий на независимый от платформы ассемблер. Внутри CLR-совместимого исполняемого модуля помещается некоторый p-код, состоящий из MSIL-инструкций. Но с помощью утилиты ildasm из p-кода можно получить текстовое представление MSIL Оно выглядит примерно так:
/* Displays the error string according to the passed in error code.
Error code must be one of the values declared by the enumeration. */
.method virtual newslot famorassem hidebysig instance
void ShowErrorText(value class ErrorCodes errorCode) synchronized il managed {
// load the appropriate error string
ldarg.0
ldfld class
[.module CountDownErrorLabel.dll] ErrorLabel CountDownForm::errorLabel
ldarg.0
ldfld class System.String[] CountDownForm::errorStrings
ldarg errorCode // load the value of the enumeration
ldelem.ref
// error string on stack, display
callvirt instance void class
[.module CountDownErrorLabel.dll] ErrorLabel::set_Text(class System.String)
ret
}
Такой код можно скомпилировать обратно в исполняемый файл с помощью утилиты ilasm. Это позволяет, например, при программировании на языке, не поддерживающем полностью всех возможностей CLR, скомпилироваться во MSIL, дизассемблировать его и добавить недостающие элементы вручную. Такие широкие возможности дизассемблирования очень порадовали бы хакеров, но Microsoft предусмотрел средства, позволяющие предотвратить дизассемблирование готового модуля. К сожалению, в beta 1 это можно сделать только с помощью повторной компиляции дизассемблированного кода из командной строки. В будущем эта опция будет встроена непосредственно в компилятор. К выходу VS.Net Microsoft обещает сделать так, чтобы приложения компилировались непосредственно при инсталляции, или даже при создании инсталляторов для конкретных платформ. Пока же компиляция в машинный код происходит только при загрузке программы.
Одно из преимуществ, дарованных нам 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 spIAntique;
HRESULT hr = spIAntique.CoCreateInstance(__uuidof(Pinto));
if (SUCCEEDED(hr)) {
CComQIPtr spICar(spIAntique);
if (spIUnknown) hr = spICar->AvoidFuelTankCollision();
if (SUCCEEDED(hr)) hr = spIAntique->Appreciate();
}
Хотя этот код, несомненно, проще и безопаснее, он все же далек от идеала, причем как с точки зрения простоты и читабельности, так и с точки зрения типобезопасности. Ведь только во время исполнения программы будет точно известно, реализует объект эти интерфейсы или нет. Несмотря на использование __uuidof, чтобы заставить COM работать с C++, нужно отключить систему проверки типов C++ на время трансляции объектных ссылок COM в C++-типы. В отличие от этого, интеграция COM с виртуальной машиной Java Microsoft позволяет написать следующее:
IAntique antique = new Pinto();
ICar car = (ICar)antique;
car.AvoidFuelTankCollision();
antique.Appreciate();
Заметьте, что этот код наиболее близок к чистому коду на C++, разве что объект создается динамически, но компоненты и нельзя создавать на стеке.
Это пример, как и самый первый, загружает компонент на основании его типа, а не имени файла. Оба примера разрешают точки входа в компонент, используя средства приведения типов, а не символьные точки входа. Первое различие – Microsoft VM for Java выполняет огромную работу по состыковке системы типов Java с системой типов COM, и программистам не приходится делать этого вручную. Это и есть движущая сила для новой платформы – обеспечить универсальную среду исполнения компонентов, которой сможет воспользоваться любой компилятор, средство или служба. Новая среда – это и есть CLR!
Читать дальшеИнтервал:
Закладка: