Visual C++ 5. Руководство разработчика. Дэвид Беннет и др. Диалектика. 1998.
Использование директивы #import в Visual C++
Использование директивы #import в Visual C++
PS. Как осуществить на VC создание документа и написать туда пару слов?
NS. В общем, нужно конвертить Word файлы в HTML программно. Помогите плиз.
VV. Возникла следующая проблема - необходимо загрузить документ Excel'а или Word'а (вместе с программами - т.е. запускается Word и загружается в него документ) и запустить в нем функцию или макрос на VBA.
MK. Имеется файл БД. Экселевский или эксесовский со след. полями… Необходимо читать и писать (добавлять и изменять) в файл. Как это лучше сделать.
SR. Мысль хорошая, только я не знаю, как связываются переменные с окнами из ресурса. Сейчас-то за меня в DoDataExchange все делают автоматически...
YI. А не подскажешь ли как работать с OLE?
Подобные вопросы часто можно встретить в конференциях Fidonet, посвящённых программированию на Visual C++. Как правило, после некоторого обсуждения, фидошная общественность приходит к мнению, что лучшее решение - использование директивы #import.
В данной статье я попытаюсь объяснить то, как работает эта директива и привести несколько примеров её использования. Надеюсь, после этого вы тоже найдёте её полезной.
Директива #import введена в Visual C++, начиная с версии 5.0. Её основное назначение облегчить подключение и использование интерфейсов COM, описание которых реализовано в библиотеках типов.
Полное описание директивы приведено в MSDN в одной единственной статье, которую можно найти по указателю, введя ключевое слово #import или по содержанию:
MSDN Library Visual C++ Documentation Using Visual C++ Visual C++ Programmer's Guide Preprocessor Reference The Preprocessor Preprocessor Directives The #import Directive
Библиотека типов представляет собой файл или компонент внутри другого файла, который содержит информацию о типе и свойствах COM объектов. Эти объекты представляют собой, как правило, объекты OLE автоматизации. Программисты, которые пишут на Visual Basic'е, используют такие объекты, зачастую сами того не замечая. Это связано с тем, что поддержка OLE автоматизации вляется неотъемлемой частью VB и при этом создаётся иллюзия того, что эти объекты также являются частью VB.
Добиться такого же эффекта при работе на C++ невозможно (да и нужно ли?), но можно упростить себе жизнь, используя классы представляющие обёртки (wrappers) интерфейса IDispatch. Таких классов в библиотеках VC имеется несколько.
- Первый из них - COleDispatchDriver, входит в состав библиотеки MFC. Для него имеется поддержка со стороны MFC ClassWizard'а, диалоговое окно которого содержит кнопку Add Class и далее From a type library. После выбора библиотеки типов и указания интерфейсов, которые мы хотим использовать, будет сгенерирован набор классов, представляющих собой обёртки выбранных нами интерфейсов. К сожалению, ClassWizard не генерирует константы, перечисленные в библиотеке типов, игнорирует некоторые интерфейсы, добавляет к именам свойств префиксы Put и Get и не отслеживает ссылок на другие библиотеки типов.
- Второй - CComDispatchDriver является частью библиотеки ATL. Я не знаю средств в VC, которые могли бы облегчить работу с этим классом, но у него есть одна особенность - с его помощью можно вызывать методы и свойства объекта не только по ID, но и по их именам, то есть использовать позднее связывание в полном объёме.
- Третий набор классов - это результат работы директивы #import.
Последний способ доступа к объектам OLE Automation является наиболее предпочтительным, так как предоставляет достаточно полный и довольно удобный набор классов.
Рассмотрим пример.
Создадим IDL-файл, описывающий библиотеку типов. Наш пример будет содержать описание одного перечисляемого типа SamplType и описание одного объекта ISamplObject, который в свою очередь будет содержать одно свойство Prop и один метод Method.
Sampl.idl:
// Sampl.idl : IDL source for Sampl.dll // This file will be processed by the MIDL tool to // produce the type library (Sampl.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ uuid(37A3AD11-F9CC-11D3-8D3C-0000E8D9FD76), version(1.0), helpstring("Sampl 1.0 Type Library") ] library SAMPLLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); typedef enum { SamplType1 = 1, SamplType2 = 2 } SamplType; [ object, uuid(37A3AD1D-F9CC-11D3-8D3C-0000E8D9FD76), dual, helpstring("ISamplObject Interface"), pointer_default(unique) ] interface ISamplObject : IDispatch { [propget, id(1)] HRESULT Prop([out, retval] SamplType *pVal); [propput, id(1)] HRESULT Prop([in] SamplType newVal); [id(2)] HRESULT Method([in] VARIANT Var,[in] BSTR Str,[out, retval] ISamplObject** Obj); }; [ uuid(37A3AD1E-F9CC-11D3-8D3C-0000E8D9FD76), helpstring("SamplObject Class") ] coclass SamplObject { [default] interface ISamplObject; }; };
После подключения соответствующей библиотеки типов с помощью директивы #import будут созданы два файла, которые генерируются в выходном каталоге проекта. Это файл sampl.tlh, содержащий описание классов, и файл sampl.tli, который содержит реализацию членнов классов. Эти файлы будут включены в проект автоматически. Ниже приведено содержимое этих файлов.
Sampl.tlh:
// Created by Microsoft (R) C/C++ Compiler Version 12.00.8472.0 (53af584f). // // sampl.tlh // // C++ source equivalent of Win32 type library Debug\sampl.dll // compiler-generated file created 03/14/00 at 20:43:40 - DO NOT EDIT! #pragma once #pragma pack(push, 8) #includenamespace SAMPLLib { // Forward references and typedefs struct __declspec(uuid("37a3ad1d-f9cc-11d3-8d3c-0000e8d9fd76")) /* dual interface */ ISamplObject; struct /* coclass */ SamplObject; // Smart pointer typedef declarations _COM_SMARTPTR_TYPEDEF(ISamplObject, __uuidof(ISamplObject)); // Type library items enum SamplType { SamplType1 = 1, SamplType2 = 2 }; struct __declspec(uuid("37a3ad1d-f9cc-11d3-8d3c-0000e8d9fd76")) ISamplObject : IDispatch { // Property data __declspec(property(get=GetProp,put=PutProp)) enum SamplType Prop; // Wrapper methods for error-handling enum SamplType GetProp ( ); void PutProp (enum SamplType pVal ); ISamplObjectPtr Method (const _variant_t & Var,_bstr_t Str ); // Raw methods provided by interface virtual HRESULT __stdcall get_Prop (enum SamplType * pVal) = 0 ; virtual HRESULT __stdcall put_Prop (enum SamplType pVal) = 0 ; virtual HRESULT __stdcall raw_Method (VARIANT Var,BSTR Str,struct ISamplObject** Obj) = 0 ; }; struct __declspec(uuid("37a3ad1e-f9cc-11d3-8d3c-0000e8d9fd76")) SamplObject; #include "debug\sampl.tli" } // namespace SAMPLLib #pragma pack(pop)
Sampl.tli:
// Created by Microsoft (R) C/C++ Compiler Version 12.00.8472.0 (53af584f). // // sampl.tli // // Wrapper implementations for Win32 type library Debug\sampl.dll // compiler-generated file created 03/14/00 at 20:43:40 - DO NOT EDIT! #pragma once // interface ISamplObject wrapper method implementations inline enum SamplType ISamplObject::GetProp ( ) { enum SamplType _result; HRESULT _hr = get_Prop(&_result); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); return _result; } inline void ISamplObject::PutProp ( enum SamplType pVal ) { HRESULT _hr = put_Prop(pVal); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); } inline ISamplObjectPtr ISamplObject::Method ( const _variant_t & Var, _bstr_t Str ) { struct ISamplObject * _result; HRESULT _hr = raw_Method(Var, Str, &_result); if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this)); return ISamplObjectPtr(_result, false); }
Первое на что следует обратить внимание - это на строчку файла sampl.tlh:
namespace SAMPLLib {
Это означает, что компилятор помещает описание классов в отдельное пространство имён, соответствующее имени библиотеки типов. Это является необходимым при использовании нескольких библиотек типов с одинаковыми именами классов, такими, например, как IDocument. При желании, имя пространства имён можно изменить или запретить его генерацию совсем:
#import "sampl.dll" rename_namespace("NewNameSAMPLLib")
#import "sampl.dll" no_namespace
Теперь рассмотрим объявление метода Method:
ISamplObjectPtr Method (const _variant_t & Var,_bstr_t Str);
Здесь мы видим использование компилятором классов поддержки COM. К таким классам относятся следующие.
- _com_error. Этот класс используется для обработки исключительных ситуаций, генерируемых библиотекой типов или каким либо другим классом поддержки (например, класс _variant_t будет генерировать это исключение, если не сможет произвести преобразование типов).
- _com_ptr_t. Этот класс определяет гибкий указатель для использования с интерфейсами COM и применяется при создании и уничтожении объектов.
- _variant_t. Инкапсулирует тип данных VARIANT и может значительно упростить код приложения, поскольку работа с данными VARIANT напрямую вляется несколько трудоёмкой.
- _bstr_t. Инкапсулирует тип данных BSTR. Этот класс обеспечивает встроенную обработку процедур распределения и освобождения ресурсов, а также других операций.
Нам осталось уточнить природу класса ISamplObjectPtr. Мы уже говорили о классе _com_ptr_t. Он используется для реализации smart-указателей на интерфейсы COM. Мы будем часто использовать этот класс, но не будем делать этого напрямую. Директива #import самостоятельно генерирует определение smart-указателей. В нашем примере это сделано следующим образом.
// Smart pointer typedef declarations
_COM_SMARTPTR_TYPEDEF(ISamplObject,__uuidof(ISamplObject));
Это объявление эквивалентно следующему:
typedef _com_ptr_t<ISamplObject,&__uuidof(ISamplObject)> ISamplObjectPtr
Использование smart-указателей позволяет не думать о счётчиках ссылок на объекты COM, т.к. методы AddRef и Release интерфейса IUnknown вызываютс автоматически в перегруженных операторах класса _com_ptr_t.
Помимо прочих этот класс имеет следующий перегруженный оператор.
Interface* operator->() const throw(_com_error);
где Interface - тип интерфейса, в нашем случае - это ISamplObject. Таким образом мы сможем обращаться к свойствам и методам нашего COM объекта. Вот как будет выглядеть пример использования директивы #import для нашего примера (красным цветом выделены места использования перегруженного оператора).
Visual C++ 5. Руководство разработчика. Дэвид Беннет и др. Диалектика. 1998.
Использование директивы #import в Visual C++