СТАТЬЯ |
28.12.00
|
(С) И. Мирончик,
независимый разработчик и
преподаватель курсов
по Delphi, C++Builder, Oracle.
E-maile EKSKHQ@Elsite.ru
Эта статья была опубликована на сайте www.borland.ru
В статье рассматривается вопрос доступа к общеизвестным приложениям Microsoft Office, таким как Word, Excel, Outlook и другим, через новый набор компонент, представленных в Delphi 5. Для начала работы нам предстоит установить на компьютере приложения Microsoft Office 97 – Excel, Word, Outlook, PowerPoint. Если считаете необходимым, то можно добавить и Access (но с ним у меня особые счеты). Ну и конечно устанавливается новый продукт Delphi 5. Кроме множества изменений в нем имеется одно, для нас сейчас необходимое – новая закладка на палитре инструментов – Servers.Через эти компоненты мы будем получать доступ к COM серверам приложений Office, использующих автоматизацию (прежде известную как OLE Automation).
Мы рассмотрим несколько примеров построения контроллеров автоматизации для создания отчетов в MS Word, производство расчетов и построение диаграмм в MS Excel, а так же формирование рассылки писем адресатам через MS Outlook.
Office – это среда, в которой большинство задач можно решать без какого-либо программирования. Но вся ценность приложений Office для разработчика заключается в том, что все, что можно сделать руками, можно сделать программным путем с использованием средств VBA (Visual Basic for Application). Кроме того, приложения Office поставляют сервера COM, которые предоставляют интерфейс доступа к приложению и его объектам. Благодаря этому, разработчик в среде Delphi имеет возможность, создав контроллер автоматизации, управлять сервером. Так как устроено приложение в Office? На самом деле приложение рассматривается как совокупность объектов со своими методами, свойствами, событиями, которые обеспечивают скелет приложения. Программист Office является не создателем приложения, как, например это делается в Delphi, а он принимает участие в создании системы документов. Таким образом, ДОКУМЕНТ, а не программа являются целью разработки.
Наследование – мощный инструмент построения нового класса, однако программистам известен еще один способ получения класса – встраивание. Как и наследование, встраивание транзитивное отношение. В объектной модели Office нет наследования в полном смысле этого слова, а есть только встраивание.
Всегда существует задающий приложение корневой объект, он всегда называется Application. Каждое приложение Office имеет свой собственный корневой объект – Word.Application, Excel.Application. Outlook приложение само является корневым объектом, несмотря на это в объект Application встраиваются все остальные объекты (участники), которые являются свойствами главного объекта. Участником могут быть свои участники и так далее. В документе методов очень много, причем самых разных, но имеются и одинаковые методы в различных приложениях Office. Среди таковых – Run, Quit, Activate; но даже и они в разных приложениях имеют различный набор параметров, а зачастую
выполняют не адекватные действия. Такое многообразие различных реализаций классов отпугивало программистов контроллеров автоматизации. Однако, используя компоненты доступа к серверам автоматизации – положение резко улучшилось. На следующем рисунке показан очень маленький фрагмент структуры объекта Microsoft Word Objects. Более полное представление об объектной архитектуре приложений Office можно найти в соответствующих файлах помощи.
Как только открывается новый документ, будь то PowerPoint, Excel, Word, автоматически создается каркас нового документа, который представляет собой набор библиотек с классами. Объекты этих классов будут доступны в данном документе. Задачей разработчика контроллера автоматизации является получить доступ к корневому объекту сервера, выстроить цепочку доступа к объектам – участникам (встроенным объектам), правильно передать параметры.
Я, наверное, утомил читателя устройством приложений Office. Но все-таки необходимо еще раз отметить, что всех объектов этого семейства не перечесть, но главные необходимо знать и уметь ими пользоваться. Фундаментальным объектом любого приложения является Application. Давайте получим к нему доступ из Delphi.
Казалось бы, ничего не произошло, запустилась форма и отобразилась на экране, на самом деле наше приложение запустило сервер автоматизации Microsoft Word, этот факт можно обнаружить, запустив на выполнение Task Manager и выбрав закладку Processes. Среди прочих процессов мы обнаруживаем WINWORD.EXE. На самом деле была приложением проделана следующая работа:
Закройте наше приложение и загляните в Task Manager - Word выгрузился из памяти.
Точно такой же результат можно было бы получить, прописав следующий код:
procedure TForm1.Button1Click(Sender: TObject); var wd:OleVariant; fileName:string; begin try fileName:=ExtractFilePath(Application.EXEName)+'report.DOC'; //Создаем объект интерфейса для доступа к серверу COM wd:=CreateOleObject('Word.Application'); //Проверка наличия методов и правильность передачи параметров будет //осуществляться на стадии выполнения приложения wd.application.documents.add; wd.application.activedocument.range.insertAfter(now); wd.application.activedocument.saveas(fileName); //выгрузаем сервер из памяти компьютера wd.application.quit(true,0); ….
Не забудьте добавить в раздел uses модуль COMOBJ.
На закладке Service находится набор компонент для доступа к серверам автоматизации, не все компоненты возвращают ссылку на объект Application, то есть могут быть получены интерфейсы для доступа к вложенным объектам, таким как Документ Word или рабочая книга Excel. Все компоненты унаследованы от класса TOLEServer, который наследует свойства класса Tcomponent. TOLEServer является базовым классом всех COM серверов, которые можно получить через среду IDE следующим образом: Project | Import Type Library. Кроме этого этот класс имеет еще несколько свойств и методов для управления связью с COM сервером. Среди таковых уже знакомое нам свойство AutoConnect, которое автоматически запускает COM сервер и производит извлечение из него интерфейса, обеспечивающего связь с контроллером. Еще одно важное свойство класса TOLEServer – ConnectKind – указывающее тип процесса, к которому устанавливается связь. Свойство используется методом Connect, который вызывается автоматически, если свойство AutoConnect истинно. Ниже описаны значения, которые может принимать ConnectKind:
Значение свойства ConnectKind |
Характеристика |
CkRunningOrNew |
Контроллер производит подключение к уже существующему процессу, или запускает новый процесс, при отсутствии такового. Этот вид взаимодействия между COM сервером и контроллером наиболее часто применяется на практике. Такое значение свойства установлено по умолчанию. |
CkNewInstance |
При соединении с сервером каждый раз создается новый экземпляр |
CkRunningInstance |
Соединение устанавливается с уже запущенным COM сервером. Если таковой отсутствует – будет создан соответствующий объект ошибки, который необходимо обработать |
CkRemote |
Это значение используется совместно со свойством RemoteMachineName, если необходимо подключиться к серверу на удаленной машине |
ckAttachToInterface |
При установке этого значения интерфейс не создается и соответственно нельзя указывать значение True для свойства AutoConnect. Соединение с сервером производится с помощью метода ConnectTo |
На последнем значении свойства ConnectKind равном ckAttachToInterface, хотелось бы остановиться более подробно. Действительно мы соединяемся с сервером через главный интерфейс, представленный в объекте Application. Но, например, возникает необходимость подключить к нашему проекту такие компоненты, как WordDocument или WordParagraphFormat, в этом случае мы просто производим подключение к уже существующему интерфейсу, а не создаем его заново. Также это может быть необходимо, когда контроллер должен отслеживать события, происходящие в COM сервере. Я предлагаю вам провести маленький эксперимент:
procedure TForm1.WordApplication1DocumentChange(Sender: TObject); begin //производим подключение к текущему документу WordDocument1.ConnectTo( WordApplication1.ActiveDocument); //Контроллер добавляет новую строку в текущий документ WordDocument1.Range.InsertAfter(#13+'Переход к документу'+#13+ WordApplication1.ActiveDocument.Get_FullName+' произведен :'+ DateTimeToStr(Now)); end; procedure TForm1.FormCreate(Sender: TObject); begin //COM сервер отображает себя на экране WordApplication1.Visible:=true; end;
После запуска приложения будет автоматически загружен Word, создайте в нем несколько новых документов и переключайтесь между ними с помощью меню Window. Вы увидите, что контроллер автоматизации добавляет новые строки в текущий активный документ. Аналогично можно управлять и сервером ExcelApplication. При создании новой рабочей книги на сервере, в контроллере будет проинициализировано событие onNewWorkBook, которое можно обработать аналогично предыдущему примеру.
Теперь давайте заглянем во внутренний мир компонент закладки Servers. Как мы уже убедились, все классы унаследованы от ToleObject. Кроме того, еще наследуется и интерфейс из библиотеки TLB. Как это происходит? Проведем следующее упражнение:
После проделанных манипуляций вам стал доступен COM сервер Excel, в состав которого входят шесть классов. Аналогичным образом восстановите компоненты для сервера Word и Outlook.
Теперь мы можем посмотреть на объявление класса TwordApplication и его предка -ToleServer:
TOleServer = class(TComponent, IUnknown) TWordApplication = class(TOleServer)
Благодаря такому объявлению, класс TwordApplication наследует свойства и методы класса Tcomponent (способен устанавливаться на палитре компонент и прочие…), а так же знает все о доступе к интерфейсам COM серверов, благодаря наследованию интерфейса IUNknown. В библиотеке типов прописаны все доступные методы и свойсва COM сервера. Когда создается контроллер автоматизации (выкладываем на форму соответствующий компонент из палитры), то приложение получает доступ к Dual Interface описанный в библиотеке типов. Dual интерфейс – есть совокупность пользовательского интерфейса, описанного в библиотеке типов и dispinterface, который доступен в момент выполнения приложения. Это реализовано с помощью COM VTABLE интерфейса, унаследованного от IDISPATCH. Использование Vtable интерфейса имеет ряд преимуществ:
Ниже приведен пример использования Dual интерфейса
procedure TForm1.FormCreate(Sender: TObject); var foo:TWordApplication; foo1:_Application; begin foo:=WordApplication1; foo1:=CoWordApplication.Create; ShowMessage(foo.UserName+#13+foo1.Get_Name); end;
Пример использования интерфейса с поздним связыванием был показан в начале этой статьи, когда доступ к COM серверу был осуществлен с помощью функции CreateOleObject. Компилятор в этом случае ничего не знает о методах и параметрах сервера, информация о них извлекается на стадии выполнения приложения, отсюда и потеря скорости выполнения приложения и всевозможные ошибки, которые компилятор не в состоянии обработать. При такой разработке приложения программист достает SDK от Microsoft office и начинает старательно изучать большие тома литературы.
Подводя итог можно говорить о том, что для доступа к COM серверу автоматизации существует три способа
Наиболее прогрессивный – первый способ, через который работают компоненты Delphi 5 для доступа к COM серверам приложений Office.
Постановка задачи: Имеется шаблон документа – shablon.DOC, подготовленный в MS Word, поля, которые должны быть заменены, помечены символом @. Необходимо прочитать данные из источника информации и заменить метки, на реальные данные, после чего распечатать полученный документ.
Воспользуемся методом Vtable . Выложим на форму компоненту WordApplication, WordDocument и кнопку Button. Для события OnClick компоненты Button1 пропишем следующий код:
procerdure TForm1.Button1Click(Sender: TObject); var //Объявление переменных, для передачи их в //eкачестве формальных параметров в сервер автоматизации Shablon,FileName,oldStr,newStr,replace,ext:OleVariant; begin Table1.Active:=false; Table1.Active:=true; Shablon:=ExtractFilePath(Application.EXEName)+'shablon.DOC'; FileName:=ExtractFilePath(Application.EXEName)+'report.DOC'; //Открываем шаблон документа WordApplication1.Documents.Open (Shablon,EmptyParam, EmptyParam,EmptyParam, EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam); //Связываем компоненту с существующим интерфейсом WordDocument1.ConnectKind:=ckAttachToInterface; WordDocument1.ConnectTo(WordApplication1.ActiveDocument); //Следующие переменные понадобятся нам //для выполнения методов сервера replace:=1; oldStr:='@1'; newStr:=DateTimeToStr(Now); //Находим в документе метки и производим их замены WordDocument1.Range.Find.Execute(oldStr,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,newStr,replace); oldStr:='@2'; newStr:=WordApplication1.UserName; WordDocument1.Range.Find.Execute(oldStr,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,newStr,replace); ……… //сохранение документа и отображение его в OLE контейнере //(предварительный просмотр) WordDocument1.SaveAs(FileName); WordDocument1.Close; OleContainer1.CreateLinkToFile(FileName,false); OleContainer1.Refresh; end;
Исходный код этого примера имеется в приложении к данной статье. Обратите внимание на передачу параметров в методы. Все параметры описаны как OleVariant и передаются в методы по ссылке (такая передача параметров характерна лишь для сервера Word, Excel и OutLook, например, принимают в основном фактические параметры). Сервер автоматизации сам будет разбираться, какой тип вы фактически подставили в качестве значения переменной, поэтому необходимо при разработке приложения особое внимание уделять хинтам, и почаще заглядывать в библиотеку типов сервера, с которым вы работаете в настоящее время. К стати, после того как мы установили компоненты, библиотеки типов разместились в директории …Delphi5\Imports\. В файлах библиотек типов можно найти, например все константы, необходимые для передачи параметров в Excel и OutLook, названия их интуитивно понятны. В модуле System – Delphi 5 описана переменная EmptyParam, которую необходимо использовать для передачи “пустышек” в качестве параметров.
var EmptyParam: OleVariant; { "Пустой параметр" который должен опционально использоваться в DUAL интерфейсе. }
Описание методов в библиотеке типов заставляет нас очень аккуратно соблюдать порядок передачи параметром, это несколько затрудняет процесс программирования, но зато Dual интерфейс работает значительно быстрее чем передача именованных параметров при позднем связывании сервера и контроллера автоматизации.
Постановка задачи: получить информацию о документе, который открывается MS Word, при этом не должны использоваться никакие компоненты для доступа к серверам автоматизации.
Создадим новое приложение и выложим на форму кнопку. Пропишем событие onDoubleClick для кнопки следующим образом:
procedure TForm1.Button1Click(Sender: TObject); var Shablon:OleVariant; word:_ApplicationDisp; begin Shablon:=ExtractFilePath(Application.EXEName)+'shablon.DOC'; word:=CoWordapplicaTion.Create as _ApplicationDisp; (Word.Documents as DocumentsDisp).Open(Shablon,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam); showmessage((Word.Application as _application).Get_Name+#13+ ((Word.Application as _application).ActiveDocument as _documentDisp).Path ); word.quit(EmptyParam,EmptyParam,EmptyParam); end;
Переменная word имеет тип ApplicatiwonDisp = dispinterface ['{00020970-0000-0000-C000-000000000046}'], который описан в библиотеки типов Word97_TLB.Pas. Строка word:=CoWordapplicaTion.Create as _ApplicationDisp; создает экземпляр сервера и возвращает DispInterface. Благодаря наличию библиотеки и жесткому приведению типов разработчик имеет возможность делать меньше ошибок, так как компилятор имеет определенную информацию о типах данных. В сравнении с использованием метода позднего связывания через функцию CreateOleObject этот метод более прогрессивный. По крайней мере, хотя бы часть информации компилятору да известна.
Подводя итог выше сказанному, можно отметить, что действительно использование компонент для доступа к COM серверам через Vtable интерфейс значительно ускоряет работу приложения и ограждает разработчика от ошибок.
Теперь поговорим о сервере Excel. После импортирования библиотеки типов, на закладке палитры компонент появились шесть иконок, основная из них ExcelApplication - компонента, обеспечивающая доступ к объекту Application сервера Excel. Следующий пример демонстрирует работу с объектом Application и WorkBook сервера Excel. Параметры серверу передаются по значению, кроме того, библиотека типов предоставляет все основные константы для работы с сервером:
const xlDays = $00000000; xlMonths = $00000001; xlYears = $00000002; procedure TForm1.FormCreate(Sender: TObject); begin ExcelApplication1.Workbooks.add (ExtractFilePath(Application.EXEName)+'customs',0); ExcelWorkbook1.ConnectTo (ExcelApplication1.ActiveWorkbook); ExcelApplication1.Visible[0]:=true; end;
Таким образом, получив доступ к серверу, мы можем легко формировать всевозможные отчеты, строить графики. Другими словами из Delphi пользоваться ресурсами MS Excel.
Так же как и в других серверах, в Outlook имеется класс Application, в который встраивается класс NameSpace. Последний предоставляет доступ к данным через объект класса MAPIFolders, представляющий собой коллекцию папок пользователя. Получив доступ к Outlook через компоненту OutLookApplication, извлекается объект доступа к MAPI папкам.
mapi:=OutlookApplication1.GetNamespace('MAPI'); for i:=1 to mapi.Folders.Count do ListBox1.Items.Add(mapi.Folders.Item(i).Name);
Добраться до соответствующей папки теперь, нет проблем, так как мы имеем дело с вложенными объектами. В приложении к статье имеется пример извлечения содержимого всех папок сервера Microsoft OutLook.
Следующий пример производит рассылку писем, извлекая адреса из таблицы базы данных. Я вам предлагаю его самостоятельно доработать и привести к “товарному виду”.
procedure TForm1.Button1Click(Sender: TObject); var mapi:NameSpace; begin Table1.Active:=false; Table1.Active:=true; //Получаем доступ к папке mapi:=OutlookApplication1.GetNamespace('MAPI'); while not Table1.Eof do begin //Подключаем объект класса TMailItem к // новому елементу исходящих писем , //для работы через Vtable интерфейс MailItem1.ConnectTo(_DMailItem(mapi.Folders.Item (olPersonal). Folders.Item(olFolderOutbox). Items.Add(olPostItem) as iDispatch)); //Наполняем новое письмо информацией MailItem1.Subject:='test for '+Table1Common_Name.Value; MailItem1.to_:=Table1Category.Value+'@elsite.ru'; Table1Graphic.SaveToFile(Table1SpeciesNo.AsString+'.BMP'); MailItem1.Attachments.Add(ExtractFilePath(Application.EXEName)+ Table1SpeciesNo.AsString+ '.BMP',1,1,Table1SpeciesName.Value); MailItem1.Body:=#13#13+'Письмо с вложенным документом '+#13+ MailItem1.SenderName; MailItem1.Sensitivity:=olConfidential ; MailItem1.FlagStatus:=olFlagMarked; //Сохраняем письмо. OutLook самостоятельно его отошлет по почте MailItem1.Save; Table1.next; end; end;
В заключении хочется еще раз подчеркнуть, что использование Vtable интерфейса значительно упростило процесс разработки контроллеров автоматизации COM серверов.
Используя мощный язык Delphi 5 и открытый интерфейс серверов MS Office можно строить очень большие и серьезные приложения, работающие совместно.
Дополнительную информацию Вы можете получить в компании Interface Ltd.
Обсудить на форуме
Inprise/Borland
Отправить
ссылку на страницу по e-mail
Interface Ltd.Отправить E-Mail http://www.interface.ru |
|
Ваши замечания и предложения отправляйте автору По техническим вопросам обращайтесь к вебмастеру Документ опубликован: 28.12.00 |