На этом шаге мы рассмотрим пример использования директивы #import.
В этом упражнении мы научимся использовать директиву #import для импорта библиотеки типов, а также применять в коде своего клиентского приложения сгенерированные типы "интеллектуальных" указателей и функции-оболочки. Мы отредактируем созданное ранее приложение EncodeHello.
#import "C:\EncodeServer\Debug\EncodeServer.dll" no_namespace
Рис.1. Добавленная строка
Убедитесь, что путь к файлу EncodeServer.dll указан верно, - на Вашем компьютере он может отличаться от указанного в примере. Вы обязаны указать атрибут no_namespace - он информирует, что все классы, созданные из библиотеки типов, будут определены в глобальном пространстве имен.
_COM_SMARTPTR_TYPEDEF(IEncoder, __uuidof(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; }
После импорта библиотеки типов можно приступить к изменению кода приложения и воспользоваться классами, созданными в результате импорта.
#include "EncodeServer.h" #include "EncodeServer_i.c"
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; }
Обратите внимание, что в этом примере код приложения размещается в собственном блоке между вызовами CoInitialize() и CoUninitialize(). Это гарантирует, что переменная pServer не выйдет из области видимости до вызова CoUninitialize(). Когда же pServer выйдет из области видимости, деструктор _com_ptr_t вызовет метод Release() через инкапсулированный указатель на IEncoder. Если же это произойдет после закрытия библиотек СОМ, результаты могут быть катастрофическими Заметьте, что по сравнению с предыдущей версией читать такой код намного легче и что он очень похож на обычный код C++, в котором СОМ не применяется. Но пусть его простота Вас не обманывает - невозможно написать высокопроизводительный и безошибочный CОМ-код без глубокого понимания основополагающих принципов этой технологии.
Со следующего шага мы начнем рассматривать повторное использование COM-объектов.