« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга


ПнВтСрЧтПтСбВс
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
    Популярное
Немного слов о кодировках

Своя кнопка в Internet Explorer

Программирование метаклассов на Python, часть 2

Использование в отчете переменных

JavaScript - полезные функции часть 8

Microsoft опубликовала подробности о сессиях Windows 7

Индексы

Оптимизация не-HTML-сайтов для поисковых серверов

Афоризмы программирования

Функция GetNextDlgTabItem




    Архив файлов



    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 196
Пользователей: 0

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



отправить ссылку другу версия для печати  Обсудить на форуме

Использование директивы #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

Игорь Ткачёв, cpplink.nm.ru




Рубрика: Visual C++




Вышел MySQL 5.1.30, первый стабильный рели....

MySQL

После публикации 29 тестовых версий анонсирован первый стабильный релиз MySQL 5.1, пригодный для промышленной эксплуатации и обеспечивающий увеличение производительности для "тяжелых" SQL запросов, по сравнению с MySQL 5.0, примерно на 15-20%. Главные новшества появившиеся в MySQL 5.1:


Подробнее... | Рубрика: MySQL | Добавлено: 28.11.2008

Тестирование параллельных программ.

Тестирование

Тестирование параллельного программного обеспечения представляет собой более сложную задачу по сравнению с тестированием последовательной программы. Программист должен знать о подводных камнях при тестировании параллельного кода, имеющихся методологиях и инструментарии.


Подробнее... | Рубрика: Тестирование | Добавлено: 28.11.2008

Архитектура AMD64 (EM64T).

Архитектура AMD

Аннотация. В статье кратко рассматривается архитектура AMD64 компании AMD и ее реализация EM64T компании Intel. Описаны особенности архитектуры, ее возможности, достоинства и недостатки.


Подробнее... | Рубрика: Архитектура AMD | Добавлено: 27.11.2008

Остальные статьи:

Платформа 2009. Определяя будущее
Windows Vista Bridge Sample Library - упра...
Оптимизация 64-битных программ
Подгрузка через AJAX HTML-кода, содержащег...
Обзор нового релиза самой мощной Ajax библ...
Firebug 1.3 и 1.4 alpha — что нового и инт...
Релиз Microsoft Silverlight 2.0. Что новог...
XML документация в C#
Курсоры в MySQL 5
Microsoft опубликовала подробности о сесси...
Microsoft делится подробностями о том, что...
Тестируем новый javascript от нового брауз...
MySQL Query Cache
Использование провайдеров компиляции в As...
Чего мы ждем от C# 4.0
Delphi 2009 и C++Builder 2009
Джоэл Спольски и Джеф Этвуд запустили новы...
Поиск кода Google /* что нового? */
10 jQuery скриптов для улучшения интерфейс...
Генераторы отчетов FastReport 4 и QuickRep...


Цитата дня (все,добавить):

Портал фрилансеров

работа на дому


    Рубрикатор

Программирование

C/С++
Обучение
Windows API
XAML
Моделирование
Паттерны
Visual Basic 7 .NET
WxWidgets
Функции WinApi
Функции С++
Разработка под Mac OS
Eiffel
Visual Studio 2008
UI дизайн
Алгоритмы
Конкурсные статьи
Turbo Pascal
Visual Studio
CASE-средства
Visual Studio 2005
Без VCL
Delphi
Тех. документация
Тестирование
Software Testing
ООП
TCP/IP
Google Android
Windows Installer
.NET Framework
Драйвера
C# C Sharp
Справка
Проектирование
Информ. системы
Visual Basic
Assembler
Оптимизация кода
Gtk+
Компоненты
Реинжиниринг
Управление проектами
Extreeme programming
Lotus Notes
Алгебраическое проектирование


Интернет технологии

PHP
Perl
ASP
WAP
Cookies
SSI
CGI
Web Servers
VB Script
DNS
CSS
XML
Html
Java Script
Java2ME
Firewall
Flash
.htaccess
Apache
VRML
Протоколы
Поисковые системы
Технология JAVA
Учебник по PHP
Учебник по JavaScript
Учебник по XML
Java Q&A
AJAX
DHTML
XHTML
Dreamweaver
Web 2.0
Python
Вебмастеру
Cisco
Ruby on Rails
Silverlight

Базы данных

Access
InterBase
MySQL
Oracle
ADO .NET
Основы SQL
Учебник по Access 2002
MS
Microsoft FoxPro
Доступ к данным
XML в MS SQL Server 2000
ODBC и MyODBC
Обучение
Caché
DB2
PostgresSQL
Sybase
Теория
Хранилища данных
Безопасность
Реляционные данные
MySQL и mSQL

Остальное:

Разное
Обзоры книг
Безопасность
Графика и дизайн
Юмор
Linux
Фракталы
Microsoft Axapta
Многоядерность
Сети
Microsoft Office
Работа
MS-DOS
Криптография
Графика и игроделание
Новости SDK
Системы защиты
Учебник по AutoCad
CVS
Windows XP
Windows Server 2003
Windows Vista
Windows 7
Мероприятия