Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Доступ к серверу Excel в MVS 2005 с помощью диспетчерского интерфейса




 

Последовательность действий для подключения Excel практически такая же, как и для сервера Word.

Шаг 1. Создание каркаса приложения.

Создадим с помощью мастера Win32 Console Application заготовку обычного консольного приложения с поддержкой MFC. Предполагается, что проекту присвоено имя UseExcel.

Шаг 2. Инициализация OLE.

Как и в случае с Word, добавим в приложение класс OleInit для инициализации служб СОМ. С учетом этого файл реализации консольного приложения UseExcel.cpp будет иметь вид, приведенный в следующем листинге.

#include "stdafx.h"

#include "UseExcel.h"

// …

 

CWinApp theApp;

 

using namespace std;

struct OleInit

{ OleInit()

{

 CoInitialize(NULL);

 TRACE("CoInitialize(NULL) executed\n");

}

~OleInit()

{

 CoUninitialize();

 TRACE("CoUninitialize() executed\n");

}

} Init;

 

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

{

     _tprintf(_T("Fatal Error: MFC initialization failed\n"));

     return nRetCode = 1;

}

 

return nRetCode;

}

Если такую программу запустить на выполнение в режиме отладки, то в окне Debug мы увидим сообщения «CoInitialize(NULL) executed» и «CoUninitialize() executed». Рекомендую это проделать практически, следуя концепции RAD (быстрой разработки программ).

Шаг 3. Генерация классов-оболочек для интерфейсов сервера Excel.

Для использования диспетчерских интерфейсов Excel надо получить доступ к их объявлениям, которые находятся, как положено в библиотеке типов, ну а сама библиотека типов, в отличие от Word, размещается в файле Excel.exe.

Для получения описаний интерфейсов и методов Excel надо выполнить команду ProjectèAdd Class, в появившемся окне выбрать Categories==MFC и Templates==MFC Class From TypeLib. Нажимаем кнопку Add и в новом появившемся окне открываем список Available type libraries (доступные библиотеки типов). В этом списке перечислены все библиотеки типов, зарегистрированные в реестре. Выбираем в выпадающем списке элемент Microsoft Excel 11.0 Object Library <1.5> (рис.1), а в списке Interfaces мы можем выбирать любые интерфейсы и включать их в список Generated classes с помощью кнопки >. Можно поступить решительно и с помощью кнопика >> сгенерировать классы для всех интерфейсов без разбору. Обратите внимание на то, что в элементах Class и File мастер показывает имена классов и заголовочных файлов, которые он будет генерировать. Закончите работу по генерации объявлений классов нажатием кнопки Finish.

Замечание. Практически того же результата мы бы смогли добиться и другим путем, выбрав в окне радиокнопку Add Class from: File, а не Registry, которая выбрана по умолчанию. Далее в поле Location с помощью диалога по открытию файла мы должны были бы указать имя файла Excel.exe

 

Рис. 1. Выбор библиотеки типов и интерфейсов

 

Шаг 4. Запуск сервера Excel.

Теперь модифицируем файл UseExcel.cpp, добавив в него код запуска Excel. Не забудьте в начало файла добавить, для начала, директиву подключения заголовочного файла CApplication.h, необходимого для запуска Excel, а также файлов comdef.h и conio.h. Кстати, вы наверное заметили на вкладке Solution Explorer в папке Header Files великое множество файлов, сгенерированных мастером.

// UseWord.cpp : Иллюстрация запуска приложения MS Word

//

#include "stdafx.h"

#include "UseWord.h"

#include <ComDef.h> // объявление класса _com_error

#include "CApplication.h"

#include <ConIO.h>

 

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

CWinApp theApp;

using namespace std;

 

char Buf[1024];

char * Rus(char * Str)

{

CharToOemA(Str,Buf); return Buf;

}

struct OleInit

{ OleInit()

{

 CoInitialize(NULL);

 TRACE("CoInitialize(NULL) executed\n");

}

~OleInit()

{

 CoUninitialize();

 TRACE("CoUninitialize() executed\n");

}

} Init;

 

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

{

     _tprintf(_T("Fatal Error: MFC initialization failed\n"));

     return nRetCode = 1;

}

try

{

     LPDISPATCH pDisp;

     LPUNKNOWN pUnk;

     CLSID clsid;

     CApplication m_app;

     /*Получение GUID приложения Word по его внешнему имени

        Word.Application, под которым Word зарегистрирован в реестре*/

     CLSIDFromProgID(L"Word.Application",&clsid);

     /*GetActiveObject позволяет получить интерфейс запущенного экземпляра сервера, если таковой есть, по его clsid*/

     if(GetActiveObject(clsid,NULL,&pUnk)==S_OK)

     {

     VERIFY(pUnk->QueryInterface(IID_IDispatch,

                                   (void **)&pDisp)==S_OK);

     m_app.AttachDispatch(pDisp);

     pUnk->Release();

     TRACE("Подключение к запущенному экземпляру”

“ Word выполнено успешно\n");

     }

     else // самостоятельно запускаем приложение Word

     if(!m_app.CreateDispatch(clsid)) throw

"Не найдено приложение Word";

     /* В этой точке мы либо подключились к уже запущенному

экземпляру Word, либо запустили свой экземпляр приложения */

     m_app.put_Visible(true);

     TRACE("Окно Word должно быть видимо\n");

     cout<<Rus("Нажми любую клавишу для завершения работы")<<endl;

     _getch();

     // Готовим параметры для метода Quit()

     VARIANT SaveChanges, DocumentFormat, Empty;

     SaveChanges.vt=VT_BOOL;

     SaveChanges.boolVal=VARIANT_TRUE;

     DocumentFormat.vt=VT_I4;

     DocumentFormat.intVal=1;

     Empty.vt=VT_EMPTY;

m_app.Quit(&SaveChanges,&DocumentFormat,&Empty);

}

catch(_com_error &ex) // обработка СОМ ошибок

{     

     AfxMessageBox(ex.ErrorMessage());

}

catch(char * msg)

{

     AfxMessageBox(CString(msg));

}

_getch();

return nRetCode;

}

 

Закомментировать import

vbcmn98.chm – справка по ActiveX для Visual Basic с примерами кода.

C:\Program Files\Microsoft Office\OFFICE11\1049\VBA*.chm (VBAAC10.chm, VBAOF11.chm, VBAOWS10.chm)

Некоторые комментарии к программе.

В файле CApplication.h нет ни одного GUID! И вообще здесь в программе используется только clsid сервера Word, который мы получаем с помощью функции CLSIDFromProgID(), и еще GUID диспетчерского интерфейса IID_IDispatch, определенный где-то в другом месте.

Функция

void COleDispatchDriver::AttachDispatch(

LPDISPATCH lpDispatch,

BOOL bAutoRelease = TRUE

);

 

подключает интерфейс lpDispatch к объекту класса COleDispatchDriver (класс CApplication является наследником этого класса). Параметр bAutoRelease указывает, должен ли интерфейс освобождаться при выходе объекта из сферы видимости. В нашем случае мы прикрепили полученный с помощью функции QueryInterface() интерфейс pDisp к объекту m_app. После этого мы сможем вызывать методы объекта m_app. Вот пример реализации одного из методов интерфейса _Application, который мы используем как метод объекта m_app:

 

LPDISPATCH _Application::GetDocuments()

{

LPDISPATCH result;

InvokeHelper(0x6, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result,

              NULL);

return result;

}

 

Объявление этого метода содержится в файле CApplication.h. Вспомогательная функция InvokeHelper(), реализация которой содержится в файле OleDisp2.cpp, подготавливает параметры для вызова функции IDispatch::Invoke(), вызывает ее и возвращает результат. Мы бы могли не добавлять в наш проект класс-оболочку CApplication, а после получения интерфейса pDisp с помощью функции QueryInterface() непосредственно вызывать метод IDispatch::Invoke(), предварительно подготовив его параметры. Но это стоило бы очень больших усилий, так как подготовка параметров является очень непростой задачей. Чтобы убедиться в этом, посмотрите на объявление метода IDispatch::Invoke():

   virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(

       /* [in] */ DISPID dispIdMember,

       /* [in] */ REFIID riid,

       /* [in] */ LCID lcid,

       /* [in] */ WORD wFlags,

       /* [out][in] */ DISPPARAMS *pDispParams,

       /* [out] */ VARIANT *pVarResult,

       /* [out] */ EXCEPINFO *pExcepInfo,

      /* [out] */ UINT *puArgErr) = 0;

 

Главную трудность представляет собой параметр pDispParams, посредством которого конкретному методу с номером dispIdMember передаются входные параметры и, после его выполнения, возвращается результат. При желании можно в режиме трассировки посмотреть на код метода COleDispatchDriver::InvokeHelper(), чтобы не хотелось этого делать «вручную». С другой стороны, можно воспользоваться функцией InvokeHelper(), для того, чтобы «облегчить себе жизнь». Вот ее прототип:

virtual void AFX_CDECL InvokeHelper(

DISPID dwDispID,

WORD wFlags,

VARTYPE vtRet,

void* pvRet,

const BYTE* pbParamInfo,

...

);

 

Параметры:

dwDispID номер (ID) свойства или метода, который должен быть вызван;
wFlags флаги, описывающие контекст вызова метода (например, DISPATCH_PROPERTYGET);
vtRet указывает на тип возвращаемого методом значения;
pvRet адрес переменной, в которую будет помещено возвращаемое методом значение (например, значение свойства). Тип этой переменной должен соответствовать vtRet;
pbParamInfo указатель на строку с завершающим нулем (null-terminated string), которая описывает типы параметров метода
список параметров переменной длины, тип и количество которых должны соответствовать указанным в pbParamInfo.

 

Функция COleDispatchDriver::InvokeHelper() на самом деле вызывает функцию COleDispatchDriver::InvokeHelperV(), которая подготавливает параметры для вызова «настоящего» метода IDispatch::Invoke() и делает его.

Если обнаруживается, что сервер Word в данное время не запущен, то это можно сделать с помощью функции COleDispatchDriver::CreateDispatch(), которая инициирует создание СОМ объекта, идентифицируемого переданным ей параметром clsid, и прикрепление к нему интерфейса нашего объекта m_app. Если функция COleDispatchDriver::CreateDispatch() выполнится успешно, то процесс WinWord.exe будет запущен, но окно приложения Word отображаться не будет и приложения Word Вы не увидите ни в списке приложений менеджера задач, ни в строке панели задач (но в списке процессов менеджера задач он будет присутствовать). Можно работать с приложением Word и в таком состоянии, а если нужно сделать его видимым, то вызовите метод put_Visible(true) сервера Word, что и сделано в приведенном выше тексте программы.

Завершить работу сервера Word может пользователь (если окно Word мы сделали видимым) или наша программа с помощью метода Quit(). Если первый параметр этого метода имеет значение true, то перед завершением Word будет выполнено автоматическое сохранение открытых в редакторе документов.

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

 

Так как многие методы серверов имеют тип VARIANT, который введен, в частности, для совместимости с Visual Basic, то необходимо ознакомиться с этим типом по подразделу конспекта. Вместо типа VARIANT в программе можно было использовать класс CComVariant, производный от tagVARIANT и являющийся классом-оболочкой для типа VARIANT. Так как этот класс имеет, в частности, несколько конструкторов, то его использование существенно упрощает текст программы:

CComVariant SaveChanges(VARIANT_TRUE),DocumentFormat(1,VT_I4),

                   Empty(VT_EMPTY);

 

Вот текст функции COleDispatchDriver::CreateDispatch(), обеспечивающей запуск сервера и получение диспетчерского интерфейса IDispatch:

 

BOOL COleDispatchDriver::CreateDispatch(REFCLSID clsid,

                                   COleException* pError)

{

ASSERT(m_lpDispatch == NULL);

m_bAutoRelease = TRUE; /* good default is to auto-release

 create an instance of the object */

LPUNKNOWN lpUnknown = NULL;

SCODE sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL | CLSCTX_REMOTE_SERVER,

                     IID_IUnknown, (LPLP)&lpUnknown);

if (sc == E_INVALIDARG)

{

// may not support CLSCTX_REMOTE_SERVER, so try without

sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL & ~CLSCTX_REMOTE_SERVER,

           IID_IUnknown, (LPLP)&lpUnknown);

}

if (FAILED(sc)) goto Failed;

 

// make sure it is running

sc = OleRun(lpUnknown);

if (FAILED(sc)) goto Failed;

 

// query for IDispatch interface

m_lpDispatch = QUERYINTERFACE(lpUnknown, IDispatch);

if (m_lpDispatch == NULL) goto Failed;

 

lpUnknown->Release();

ASSERT(m_lpDispatch != NULL);

return TRUE;

 

Failed:

RELEASE(lpUnknown);

if (pError != NULL) pError->m_sc = sc;

return FALSE;

}

 

Как найти информацию по объектам, предоставляемым серверами MS Office?

Во-первых, это документация MSDN. Путь к нужному разделу справки в MSDN для MVS-2005 показан на рис. 2. В справочной системе описаны как объектные модели приложений MS Office, так и приведены примеры использования методов классов на языке VBA.

Во-вторых, можно использовать такой сервис приложений MS Office, как запись макросов. Например, чтобы воспользоваться этим средством в MS Word, надо проделать такие операции:

4. Запустить на выполнение MS Word и включить запись макроса – Сервис/Макрос/Начать запись. В появившемся диалоговом окне можно изменить имя макроса и сферу его применения

5. Выполнить «вручную» те операции, которые необходимо запрограммировать. При этом возможно с помощью кнопок панели управления макроса приостановить или отменить его запись

6. После завершения всех операций остановить запись и открыть текст макроса, например, с помощью команды Сервис/Макрос/Редактор Visual Basic. Сгенерированный текст макроса можно использовать как шаблон для записи текста программы.

 

 

Рис. 2. Путь к разделу справки по программированию доступа к сервисам MSWord










Последнее изменение этой страницы: 2018-04-12; просмотров: 411.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...