Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 июля!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Слушай, дружище, зачем так мучиться с этим языком С++, ты ведь не Билл Гейтс. Возьми тот же Python и программируй, он кроссплатформенный, под Windows тоже работает. Я сам давно заметил: то что на Си пишешь в страницу кода, на питоне решается в одну-две строки. При том, питон намного проще, я его сам недавно изучил по видеокурсу вот этому. Кстати, автор отлично там объясняет. Буквально день-два и уже будешь писать на нём, чего не скажешь про сложный С++.

Использование директивы #import в Visual C++

  • Как осуществить на VC создание документа и написать туда пару слов?
  • В общем, нужно конвертить Word файлы в HTML программно. Помогите плиз.
  • Возникла следующая проблема - необходимо загрузить документ Excel'а или Word'а (вместе с программами - т.е. запускается Word и загружается в него документ) и запустить в нем функцию или макрос на VBA.
  • Имеется файл БД. Экселевский или эксесовский со след. полями: Необходимо читать и писать (добавлять и изменять) в файл. Как это лучше сделать.
  • Мысль хорошая, только я не знаю, как связываются переменные с окнами из ресурса. Сейчас-то за меня в DoDataExchange все делают автоматически:
  • А не подскажешь ли как работать с 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)
 
 
 
 #include <comdef.h>
 
 
 
 namespace 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 для нашего примера (красным цветом выделены места использования перегруженного оператора).
#import "sampl.dll"
 
 
 
 void SamplFunc ()
 
 {
 
     SAMPLLib::ISamplObjectPtr obj;
 
     obj.CreateInstance(L"SAMPLLib.SamplObject");
 
 
 
     SAMPLLib::ISamplObjectPtr obj2 = obj< color=red>->Method(1l,L"12345");
 
     obj< color=red>->Prop = SAMPLLib::SamplType2;
 
     obj2< color=red>->Prop = obj< color=red>->Prop;
 
 }

Как видно из примера создавать объекты COM с использованием классов, сгенерированных директивой #import, достаточно просто. Во-первых, необходимо объявить smart-указатель на тип создаваемого объекта. После этого для создания экземпляра нужно вызвать метод CreateInstance класса _com_ptr_t, как показано в следующих примерах:

    SAMPLLib::ISamplObjectPtr obj;
 
     obj.CreateInstance(L"SAMPLLib.SamplObject");
 
 или
 
     obj.CreateInstance(__uuidof(SamplObject));

Можно упростить этот процесс, передавая идентификатор класса в конструктор указателя:

    SAMPLLib::ISamplObjectPtr obj(L"SAMPLLib.SamplObject");
 
 или
 
     SAMPLLib::ISamplObjectPtr obj(__uuidof(SamplObject));

Прежде чем перейти к примерам, нам необходимо рассмотреть обработку исключительных ситуаций. Как говорилось ранее, директива #import использует для генерации исключительных ситуаций класс _com_error. Этот класс инкапсулирует генерируемые значения HRESULT, а также поддерживает работу с интерфейсом IErrorInfo для получения более подробной информации об ошибке. Внесём соответствующие изменения в наш пример:

#import "sampl.dll"
 
 
 
 void SamplFunc ()
 
 {
 
     try {
 
         using namespace SAMPLLib;
 
         ISamplObjectPtr obj(L"SAMPLLib.SamplObject");
 
         ISamplObjectPtr obj2 = obj->Metod(1l,L"12345");
 
         obj->Prop = SAMPLLib::SamplType2;
 
         obj2->Prop = obj->Prop;
 
     } catch (_com_error& er) {
 
         printf("_com_error:\n"
 
                "Error       : %08lX\n"
 
                "ErrorMessage: %s\n"
 
                "Description : %s\n"
 
                "Source      : %s\n",
 
                er.Error(),
 
                (LPCTSTR)_bstr_t(er.ErrorMessage()),
 
                (LPCTSTR)_bstr_t(er.Description()),
 
                (LPCTSTR)_bstr_t(er.Source()));
 
     }
 
 }

При изучении файла sampl.tli хорошо видно как директива #import генерирует исключения. Это происходит всегда при выполнении следующего условия:

if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));

Этот способ, безусловно, является универсальным, но могут возникнуть некоторые неудобства. Например, метод MoveNext объекта Recordset ADO возвращает код, который не является ошибкой, а лишь индицирует о достижении конца набора записей. Тем не менее, мы получим исключение. В подобных случаях придётся использовать либо вложенные операторы try {} catch, либо корректировать wrapper, внося обработку исключений непосредственно в тело сгенерированных процедур. В последнем случае, правда, придется подключать файлы *.tlh уже обычным способом, через #include. Но делать это никто не запрещает.

Наконец, настало время рассмотреть несколько практических примеров. Я приведу четыре примера работы с MS Word, MS Excel, ADO DB и ActiveX Control. Первые три примера будут обычными консольными программами, в последнем примере я покажу, как можно заменить класс COleDispatchDriver сгенерированный MFC Class Wizard'ом на классы полученные директивой #import.

Для первых двух примеров нам понадобиться файл следующего содержания:

// Office.h
 
 
 
 #define Uses_MSO2000
 
 
 
 #ifdef Uses_MSO2000
 
 // for MS Office 2000
 
 #import "C:\Program Files\Microsoft Office\Office\MSO9.DLL"
 
 #import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
 
 #import "C:\Program Files\Microsoft Office\Office\MSWORD9.OLB" \
 
         rename("ExitWindows","_ExitWindows")
 
 #import "C:\Program Files\Microsoft Office\Office\EXCEL9.OLB" \
 
         rename("DialogBox","_DialogBox") \
 
         rename("RGB","_RGB") \
 
         exclude("I","IPicture")
 
 #import "C:\Program Files\Common Files\Microsoft Shared\DAO\DAO360.DLL" \
 
         rename("EOF","EndOfFile") rename("BOF","BegOfFile")
 
 #import "C:\Program Files\Microsoft Office\Office\MSACC9.OLB"
 
 #else
 
 // for MS Office 97
 
 #import "C:\Program Files\Microsoft Office\Office\MSO97.DLL"
 
 #import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBEEXT1.OLB"
 
 #import "C:\Program Files\Microsoft Office\Office\MSWORD8.OLB" \
 
         rename("ExitWindows","_ExitWindows")
 
 #import "C:\Program Files\Microsoft Office\Office\EXCEL8.OLB" \
 
         rename("DialogBox","_DialogBox") \
 
         rename("RGB","_RGB") \
 
         exclude("I","IPicture")
 
 #import "C:\Program Files\Common Files\Microsoft Shared\DAO\DAO350.DLL" \
 
         rename("EOF","EndOfFile")
 
         rename("BOF","BegOfFile")
 
 #import "C:\Program Files\Microsoft Office\Office\MSACC8.OLB"
 
 #endif
 
 

Этот файл содержит подключение библиотек типов MS Word, MS Excel и MS Access. По умолчанию подключаются библиотеки для MS Office 2000, если на вашем компьютере установлен MS Office 97, то следует закомментировать строчку

#define Uses_MSO2000

Если MS Office установлен в каталог отличный от "C:\Program Files\Microsoft Office\Office\", то пути к библиотекам также следует подкорректировать. Обратите внимание на атрибут rename, его необходимо использовать, когда возникают конфликты имён свойств и методов библиотеки типов с препроцессором. Например, функция ExitWindows объявлена в файле winuser.h как макрос:

#define ExitWindows(dwReserved,Code) ExitWindowsEx(EWX_LOGOFF,0xFFFFFFFF)

В результате, там, где препроцессор встретит имя ExitWindows, он будет пытаться подставлять определение макроса. Этого можно избежать при использовании атрибута rename, заменив такое имя на любое другое.

MS Word

// console.cpp : Defines the entry point for the console application.
 
 
 
 #include "stdafx.h"
 
 #include <stdio.h>
 
 #include "Office.h"
 
 
 
 void main()
 
 {
 
   ::CoInitialize(NULL);
 
   try {
 
     using namespace Word;
 
     _ApplicationPtr word(L"Word.Application");
 
     word->Visible = true;
 
     word->Activate();
 
 
 
     // создаём новый документ
 
     _DocumentPtr wdoc1 = word->Documents->Add();
 
 
 
     // пишем пару слов
 
     RangePtr range = wdoc1->Content;
 
     range->LanguageID = wdRussian;
 
     range->InsertAfter("Пара слов");
 
 
 
     // сохраняем как HTML
 
     wdoc1->SaveAs(&_variant_t("C:\\MyDoc\\test.htm"),
 
                   &_variant_t(long(wdFormatHTML)));
 
                // иногда придется прибегать к явному преобразованию типов,
 
                // т.к. оператор преобразования char* в VARIANT* не определён
 
 
 
     // открывает документ test.doc
 
     _DocumentPtr wdoc2 = word->Documents->Open(&_variant_t("C:\\MyDoc\\test.doc"));
 
     // вызываем макрос
 
     word->Run("Macro1");
 
 
 
   } catch (_com_error& er) {
 
     char buf[1024];
 
     sprintf(buf,"_com_error:\n"
 
                 "Error       : %08lX\n"
 
                 "ErrorMessage: %s\n"
 
                 "Description : %s\n"
 
                 "Source      : %s\n",
 
                 er.Error(),
 
                 (LPCTSTR)_bstr_t(er.ErrorMessage()),
 
                 (LPCTSTR)_bstr_t(er.Description()),
 
                 (LPCTSTR)_bstr_t(er.Source()));
 
 
 
     CharToOem(buf,buf); // только для косольных приложений
 
     printf(buf);
 
   }
 
   ::CoUninitialize();
 
 }

MS Excel

// console.cpp : Defines the entry point for the console application.
 
 
 
 #include "stdafx.h"
 
 #include <stdio.h>
 
 #include "Office.h"
 
 
 
 void main()
 
 {
 
   ::CoInitialize(NULL);
 
   try {
 
     using namespace Excel;
 
     _ApplicationPtr excel("Excel.Application");
 
     excel->Visible[0] = true;
 
 
 
     // создаём новую книгу
 
     _WorkbookPtr  book  = excel->Workbooks->Add();
 
     // получаем первый лист (в VBA нумерация с единицы)
 
     _WorksheetPtr sheet = book->Worksheets->Item[1L];
 
                        // Аналогичная конструкция на VBA выглядит так:
 
                        // book.Worksheets[1]
 
                        // В библиотеке типов Item объявляется как метод или
 
                        // свойство по умолчанию (id[0]), поэтому в VB его
 
                        // можно опускать. На C++ такое, естественно, не пройдёт.
 
 
 
     // заполняем ячейки
 
     sheet->Range["B2"]->FormulaR1C1 = "Строка 1";
 
     sheet->Range["C2"]->FormulaR1C1 = 12345L;
 
     sheet->Range["B3"]->FormulaR1C1 = "Строка 2";
 
     sheet->Range["C3"]->FormulaR1C1 = 54321L;
 
     // заполняем и активизируем итоговую строку
 
     sheet->Range["B4"]->FormulaR1C1 = "Итого:";
 
     sheet->Range["C4"]->FormulaR1C1 = "=SUM(R[-2]C:R[-1]C)";
 
     sheet->Range["C4"]->Activate();
 
 
 
     // типа делаем красиво :o)
 
     sheet->Range["A4:D4"]->->ColorIndex = 27L;
 
     sheet->Range["A4:D4"]->Interior->ColorIndex = 5L;
 
              // Постфикс L говорит, что константа является числом типа long.
 
              // Вы всегда должны приводить числа к типу long или short при
 
              // преобразованию их к _variant_t, т.к. преобразование типа int
 
              // к _variant_t не реализовано. Это вызвано не желанием
 
              // разработчиков компилятора усложнить нам жизнь, а спецификой
 
              // самого типа int.
 
 
 
   } catch (_com_error& er) {
 
     char buf[1024];
 
     sprintf(buf,"_com_error:\n"
 
                 "Error       : %08lX\n"
 
                 "ErrorMessage: %s\n"
 
                 "Description : %s\n"
 
                 "Source      : %s\n",
 
                 er.Error(),
 
                 (LPCTSTR)_bstr_t(er.ErrorMessage()),
 
                 (LPCTSTR)_bstr_t(er.Description()),
 
                 (LPCTSTR)_bstr_t(er.Source()));
 
 
 
     CharToOem(buf,buf); // только для косольных приложений
 
     printf(buf);
 
   }
 
   ::CoUninitialize();
 
 }

ADO DB

// console.cpp : Defines the entry point for the console application.
 
 
 
 #include "stdafx.h"
 
 #include <stdio.h>
 
 
 
 #import "C:\Program Files\Common Files\System\ado\msado20.tlb" \
 
         rename("EOF","ADOEOF") rename("BOF","ADOBOF")
 
      // оператор rename необходим, т.к. EOF определён как макрос
 
      // в файле stdio.h
 
 using namespace ADODB;
 
 
 
 void main()
 
 {
 
   ::CoInitialize(NULL);
 
   try {
 
     // открываем соединение с БД
 
     _ConnectionPtr con("ADODB.Connection");
 
     con->Open(L"Provider=Microsoft.Jet.OLEDB.3.51;"
 
               L"Data Source=Elections.mdb","","",0);
 
 
 
     // открываем таблицу
 
     _RecordsetPtr rset("ADODB.Recordset");
 
     rset->Open(L"ElectTbl",(IDispatch*)con,
 
                adOpenDynamic,adLockOptimistic,adCmdTable);
 
 
 
     FieldsPtr flds = rset->Fields;
 
 
 
     // добавляем
 
     rset->AddNew();
 
     flds->Item[L"Фамилия"]             ->Value = L"Пупкин";
 
     flds->Item[L"Имя"]                 ->Value = L"Василий";
 
     flds->Item[L"Отчество"]            ->Value = L"Карлович";
 
     flds->Item[L"Голосовал ли"]        ->Value = false;
 
     flds->Item[L"За кого проголосовал"]->Value = L"Против всех";
 
     rset->Update();
 
 
 
     // подменяем
 
     flds->Item[L"Голосовал ли"]        ->Value = true;
 
     flds->Item[L"За кого проголосовал"]->Value = L"За наших";
 
     rset->Update();
 
 
 
     // просмотр
 
     rset->MoveFirst();
 
     while (!rset->ADOEOF) {
 
       char buf[1024];
 
       sprintf(buf,"%s %s %s: %s - %s\n",
 
               (LPCTSTR)_bstr_t(flds->Item[L"Фамилия"]->Value),
 
               (LPCTSTR)_bstr_t(flds->Item[L"Имя"]->Value),
 
               (LPCTSTR)_bstr_t(flds->Item[L"Отчество"]->Value),
 
               (bool)flds->Item[L"Голосовал ли"]->Value? "Да": "Нет",
 
               (LPCTSTR)_bstr_t(flds->Item[L"За кого проголосовал"]->Value));
 
 
 
       CharToOem(buf,buf);
 
       printf(buf);
 
       rset->MoveNext();
 
     }
 
   } catch (_com_error& er) {
 
     char buf[1024];
 
     sprintf(buf,"_com_error:\n"
 
                 "Error       : %08lX\n"
 
                 "ErrorMessage: %s\n"
 
                 "Description : %s\n"
 
                 "Source      : %s\n",
 
                 er.Error(),
 
                 (LPCTSTR)_bstr_t(er.ErrorMessage()),
 
                 (LPCTSTR)_bstr_t(er.Description()),
 
                 (LPCTSTR)_bstr_t(er.Source()));
 
 
 
     CharToOem(buf,buf); // только для косольных приложений
 
     printf(buf);
 
   }
 
   ::CoUninitialize();
 
 }

AciveX Control

Для этого примера нам понадобится любое оконное приложение.
ActiveX Control'ы вставляются в диалог обычно через Components and Controls Gallery:
Меню-Project-Add_To_Project-Components_and_Controls-Registered_ActiveX_Controls.

Нам в качестве примера вполне подойдёт Microsoft FlexGrid Control. Нажмите кнопку Insert для добавления его в проект, в появившемся окне Confirm Classes оставьте галочку только возле элемента CMSFlexGrid и смело жмите OK. В результате будут сформированы два файла msflexgrid.h и msflexgrid.cpp, большую часть содержимого которых нам придётся удалить. После всех изменений эти файлы будут иметь следующий вид:

msflexgrid.h

// msflexgrid.h
 
 
 
 #ifndef __MSFLEXGRID_H__
 
 #define __MSFLEXGRID_H__
 
 
 
 #if _MSC_VER > 1000
 
 #pragma once
 
 #endif // _MSC_VER > 1000
 
 
 
 #pragma warning(disable:4146)
 
 #import <MSFLXGRD.OCX>
 
 
 
 class CMSFlexGrid : public CWnd
 
 {
 
 protected:
 
     DECLARE_DYNCREATE(CMSFlexGrid)
 
 public:
 
 
 
   MSFlexGridLib::IMSFlexGridPtr I; // доступ к интерфейсу
 
   void PreSubclassWindow ();       // инициализация I
 
 };
 
 
 
 //{{AFX_INSERT_LOCATION}}
 
 
 
 #endif

msflexgrid.cpp

// msflexgrid.cpp
 
 
 
 #include "stdafx.h"
 
 #include "msflexgrid.h"
 
 
 
 IMPLEMENT_DYNCREATE(CMSFlexGrid, CWnd)
 
 
 
 void CMSFlexGrid::PreSubclassWindow ()
 
 {
 
   CWnd::PreSubclassWindow();
 
 
 
   MSFlexGridLib::IMSFlexGrid *pInterface = NULL;
 
 
 
   if (SUCCEEDED(GetControlUnknown()->QueryInterface(I.GetIID(),
 
                                          (void**)&pInterface))) {
 
     ASSERT(pInterface != NULL);
 
     I.Attach(pInterface);
 
   }
 
 }

Теперь вставим элемент в любой диалог, например CAboutDlg. В диалог добавим переменную связанную с классом CMSFlexGrid и метод OnInitDialog, текст которого приведён ниже. При вызове диалога в наш FlexGrid будут добавлены два элемента:

BOOL CAboutDlg::OnInitDialog()
 
 {
 
   CDialog::OnInitDialog();
 
 
 
   m_grid.I->AddItem("12345");
 
   m_grid.I->AddItem("54321");
 
 
 
   return TRUE;
 
 }

В заключении, позволю себе высказать ещё несколько замечаний.

  • Всегда внимательно изучайте файлы *.tlh. Отчасти они могут заменить документацию, а если её нет, то это единственный источник информации (кроме, конечно, OLE/COM Object Viewer).
  • Избегайте повторяющихся сложных конструкций. Например, можно написать так:
    book->Worksheets->Item[1L]->Range["B2"]->FormulaR1C1 = "Строка 1";
     
     book->Worksheets->Item[1L]->Range["C2"]->FormulaR1C1 = 12345L;
    Но в данном случае вы получите неоправданное замедление из-за лишнего межзадачного взаимодействия, а в случае DCOM - сетевого взаимодействия. Лучше написать так:
    _WorksheetPtr sheet = book->Worksheets->Item[1L];
     
     sheet->Range["B2"]->FormulaR1C1 = "Строка 1";
     
     sheet->Range["C2"]->FormulaR1C1 = 12345;
  • При работе с MS Office максимально используйте возможности VBA для подготовки и тестирования вашего кода. Приведённые примеры я сочинил за пару минут, просто включив запись макроса, после чего скопировал полученный код в свою программу, слегка оптимизировал его и адаптировал для C++. Например, я понятия не имел, что объект Range имеет свойство FormulaR1C1, тем не менее, я получил то, что хотел.
  • Будьте внимательны с версиями библиотек типов. К примеру, в MS Word 2000 появилась новая версия метода Run. Старая тоже осталась, но она имеет теперь название RunOld. Если вы используете MS Word 2000 и вызываете метод Run, то забудьте о совместимости с MS Word 97, метода с таким ID в MS Word 97 просто нет. Используйте вызов RunOld и проблем не будет, хотя если очень хочется можно всегда проверить номер версии MS Word.
  • Бывают глюки :o(. Сразу замечу, что это не связано с самой директивой #import. Например, при использовании класса COleDispatchDriver с MSADODC.OCX у меня всё прекрасно работало, после того как я стал использовать директиву #import, свойство ConnectionString отказалось возвращать значение. Дело в том, что директива #import генерирует обёртку, использу dual-интерфейс объекта, а класс COleDispatchDriver вызывает ConnectionString через IDispatch::Invoke. Ошибка, видимо, в реализации самого MSADODC.OCX. После изменения кода вызова свойства всё заработало:
    inline _bstr_t IAdodc::GetConnectionString () {
     
         BSTR _result;
     
         HRESULT _hr = _com_dispatch_propget(this,0x01,VT_BSTR,&_result);
     
     //  HRESULT _hr = get_ConnectionString(&_result);
     
         if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
     
         return _bstr_t(_result, false);
     
     }
  • В результате раскрутки библиотек типов MS Office, компилятор нагенерирует вам в выходной каталог проекта около 12! Mb исходников. Всё это он потом, естественно, будет компилировать. Если вы не являетесь счастливым обладателем PIII, то наверняка заметите некоторые тормоза. В таких случаях я стараюсь выносить в отдельный файл всю работу, связанную с подобными библиотеками типов. Кроме того, компилятор может генерировать обёртки классов каждый раз после внесения изменений в файл, в который включена директива #import. Представьте, что будет, если после каждого нажатия клавиши будут заново генерироваться все 12 Mb? Лучше вынести объявление директивы #import в отдельный файл и подключать его через #include.

Удачи в бою.

Литература:

  • Visual C++ 5. Руководство разработчика. Дэвид Беннет и др. Диалектика. 1998


<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS