Студия web-дизайна Хостмэйк
Наши работыКонтактыО компанииОтзывыГлоссарийСтатьи

Использование директивы #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 Debugsampl.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 "debugsampl.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 Debugsampl.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 : %08lXn"
    "ErrorMessage: %sn"
    "Description : %sn"
    "Source : %sn",
    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 FilesMicrosoft OfficeOfficeMSO9.DLL"
    #import "C:Program FilesCommon FilesMicrosoft SharedVBAVBA6VBE6EXT.OLB"
    #import "C:Program FilesMicrosoft OfficeOfficeMSWORD9.OLB"
    rename("ExitWindows","_ExitWindows")
    #import "C:Program FilesMicrosoft OfficeOfficeEXCEL9.OLB"
    rename("DialogBox","_DialogBox")
    rename("RGB","_RGB")
    exclude("I","IPicture")
    #import "C:Program FilesCommon FilesMicrosoft SharedDAODAO360.DLL"
    rename("EOF","EndOfFile") rename("BOF","BegOfFile")
    #import "C:Program FilesMicrosoft OfficeOfficeMSACC9.OLB"
    #else
    // for MS Office 97
    #import "C:Program FilesMicrosoft OfficeOfficeMSO97.DLL"
    #import "C:Program FilesCommon FilesMicrosoft SharedVBAVBEEXT1.OLB"
    #import "C:Program FilesMicrosoft OfficeOfficeMSWORD8.OLB"
    rename("ExitWindows","_ExitWindows")
    #import "C:Program FilesMicrosoft OfficeOfficeEXCEL8.OLB"
    rename("DialogBox","_DialogBox")
    rename("RGB","_RGB")
    exclude("I","IPicture")
    #import "C:Program FilesCommon FilesMicrosoft SharedDAODAO350.DLL"
    rename("EOF","EndOfFile")
    rename("BOF","BegOfFile")
    #import "C:Program FilesMicrosoft OfficeOfficeMSACC8.OLB"
    #endif

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

    #define Uses_MSO2000

    Если MS Office установлен в каталог отличный от "C:Program
    FilesMicrosoft OfficeOffice"
    , то пути к библиотекам также следует
    подкорректировать. Обратите внимание на атрибут 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 : %08lXn"
    "ErrorMessage: %sn"
    "Description : %sn"
    "Source : %sn",
    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 : %08lXn"
    "ErrorMessage: %sn"
    "Description : %sn"
    "Source : %sn",
    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 FilesCommon FilesSystemadomsado20.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 - %sn",
    (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 : %08lXn"
    "ErrorMessage: %sn"
    "Description : %sn"
    "Source : %sn",
    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


    Автор: Игорь Ткачёв
    mailto:it@hotmail.ru
    www.cpplink.nm.ru

    16.02.2006

    Телефон

    +7 8636 237-836

    Поиск

    VSESMI.ru — новости в СМИ.
    Один из больших по объему информации проектов, работающих под управлением HostCMS.

    Tur-Hotel.ru — отзывы об отелях
    На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.