На этом шаге мы рассмотрим пример использования директивы #import.
В этом упражнении мы научимся использовать директиву #import для импорта библиотеки типов, а также
применять в коде своего клиентского приложения сгенерированные типы "интеллектуальных" указателей и
функции-оболочки. Мы отредактируем созданное ранее приложение EncodeHello.
- Импорт библиотеки типов EncodeServer.
- Вернитесь к проекту EncodeHello. В FileView найдите и откройте файл StdAfx.h.
- Перед строкой с комментарием //{{AFX_INSERT_LOCATION}} вставьте строку следующего вида:
#import "C:\EncodeServer\Debug\EncodeServer.dll" no_namespace
Рис.1. Добавленная строка
Убедитесь, что путь к файлу EncodeServer.dll указан верно, - на Вашем компьютере он может отличаться от
указанного в примере. Вы обязаны указать атрибут no_namespace - он информирует, что все классы,
созданные из библиотеки типов, будут определены в глобальном пространстве имен. - Сохраните и закройте файл StdAfx.h. В FileView щелкните правой кнопкой мыши файл StdAfx.cpp и в открывшемся контекстном
меню выберите Compile StdAfx.cpp. - После завершения компиляции найдите в папке EncodeHello\Debug файлы EncodeServer.tlh и
EncodeServer.tli. Просмотрите TLH-файл. Обратите внимание на объявление "интеллектуального" указателя IEncoderPtr в такой строке:_COM_SMARTPTR_TYPEDEF(IEncoder, __uuidof(IEncoder));
А также на следующие функции-члены, объявленные в определении структуры IEncoder:
// // Данные для свойства // __declspec(property(get=GetKey,put=PutKey)) short Key; // // Методы оболочки для обработки исключений // _bstr_t EncodeString ( _bstr_t instring ); short GetKey ( ); void PutKey ( short pVal ); // // "Сырые" методы интерфейса // virtual HRESULT __stdcall raw_EncodeString ( BSTR instring, BSTR * outstring ) = 0; virtual HRESULT __stdcall get_Key ( short * pVal ) = 0; virtual HRESULT __stdcall put_Key ( short pVal ) = 0;
"Сырые" методы в конце кода похожи на аналогичные методы в файле EncodeServer.h, сформированном MIDL-компилятором. Тем не
менее методы оболочки больше напоминают функции-члены обычного класса C++. Обратите внимание, что для объявления
переменной-члена Key используется ключевое слово __declspec(property). Это позволяет пользователю класса
вызывать функции Get() и Put(), просто размещая переменную-член Key справа или слева от оператора присваивания.
Также обратите внимание, что эти функции используют класс _bstr_t в качестве типа параметра и возвращаемого значения.
Реализация функций-оболочек находится в файле EncodeServer.tli В теле приведенной ниже функции GetKey() показано, как в
качестве возвращаемого значения передается параметр [out, retval], а значение HRESULT перехватывается и возвращается как
возможное исключениеinline short IEncoder::GetKey ( ) { short _result; HRESULT _hr = get_Key(&_result); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); return _result; }
После импорта библиотеки типов можно приступить к изменению кода приложения и воспользоваться классами, созданными в результате импорта. - Модификация кода приложения EncodeHello.
- Удалите следующие директивы #include в начале файла EncodeHello.срр:
#include "EncodeServer.h" #include "EncodeServer_i.c"
- Перепишите функцию main() приложения EncodeHello как показано ниже:
int main(int argc, char* argv[]) { CoInitialize( NULL ); { IEncoderPtr pServer; HRESULT hr = pServer.CreateInstance( __uuidof( Encoder ) ); if( SUCCEEDED( hr ) ) { short nKey = 1; cout << "Enter a code key between -5 and 5: "; cin >> nKey; _bstr_t bstrHello = "Hello World!"; _bstr_t bstrCodedHello; try { pServer->Key = nKey; bstrCodedHello = pServer->EncodeString( bstrHello ); cout << "\n" << (const char *) bstrCodedHello << "\n\n"; } catch( _com_error e ) { cout << e.ErrorMessage() << "\n\n"; } } } ::CoUninitialize(); return 0; }
Текст этого приложения можно взять здесь (162,0 Кб).
Обратите внимание, что в этом примере код приложения размещается в собственном блоке между вызовами
CoInitialize() и CoUninitialize(). Это гарантирует, что переменная pServer не выйдет из области
видимости до вызова CoUninitialize(). Когда же pServer выйдет из области видимости, деструктор
_com_ptr_t вызовет метод Release() через инкапсулированный указатель на IEncoder. Если же
это произойдет после закрытия библиотек СОМ, результаты могут быть катастрофическими
Заметьте, что по сравнению с предыдущей версией читать такой код намного легче и что он очень похож на обычный
код C++, в котором СОМ не применяется. Но пусть его простота Вас не обманывает - невозможно
написать высокопроизводительный и безошибочный CОМ-код без глубокого понимания основополагающих
принципов этой технологии.
Со следующего шага мы начнем рассматривать повторное использование COM-объектов.