...
...

Microsoft Office и Delphi 5. Часть 1

Microsoft Office и Delphi 5. Часть 1 В статье рассматриваются вопросы доступа к общеизвестным приложениям MS Office, таким как Word, Excel, Outlook, через набор компонент Servers, представленных в Delphi 5. Упомянутая закладка Servers, как бы это сказать, представляет собой "темную лошадку", что ли. Не часто к ней обращаются программисты, может, из-за неприязни к известной корпорации, может, из-за недостатка документации по компонентам, представленным на этой закладке. Тем не менее, мы сделаем попытку эффективно и максимально полезно использовать всю доступную информацию и наши знания для доступа к COM серверам приложений Office, использующих автоматизацию, ранее известную как OLE Automation.
В качестве примеров я приведу несколько простых, но, тем не менее, прозрачных для понимания исходных кодов программ, которые покажут на примере некоторые особенности работы с контроллерами автоматизации. Так, мы создадим несколько отчетов в MS Word, что-нибудь рассчитаем и построим диаграмму в MS Excel и попробуем отослать письмо адресату через MS Outlook.
Объекты MS Office. Office — это среда, в которой подавляющее большинство задач можно решать без программирования. Но вся ценность приложений Офиса для разработчика заключается в том, что все, что можно сделать руками, можно сделать и программным путем с использованием VBA — Visual Basic for Application. Кроме того, приложения Офиса являются серверами СОМ, которые предоставляют интерфейсы доступа к приложению и его объектам. Используя это, разработчик может создать в среде Дельфи контроллер автоматизации для управления сервером. Но чтобы создать такой контроллер, необходимо сначала задаться вопросом: что представляет из себя приложение MS Office? Вот об этом мы сейчас и поговорим.
На самом деле, как и любое другое приложение, приложение Офиса рассматривается как совокупность объектов со своими методами, свойствами и событиями, которые представляют собой скелет приложения. При этом программист не является создателем приложения (как это случается в Дельфи), а лишь создает систему из документов. Итак, целью нашего рассмотрения является не столько само приложение, сколько документ, с которым оно работает.
Имея объектную структуру, приложение MS Office обладает всеми чертами объектной модели. То есть, в объектной модели Office существует и наследование, и встраивание классов. Наследование, конечно, очень мощный инструмент, но в модели Office того наследования, как мы привыкли его понимать по объектно-ориентированному программированию, нет. Но присутствует встраивание. Как всегда, существует некий базовый, прародительский объект, который является "отцом" всех встраиваемых объектов. Такой объект называется Application. Схема показывает упрощенную иерархию таких объектов:

Следует заметить, что корневой объект, задающий приложение, существует всегда. И каждое приложение MS Office имеет свой корневой объект: Word.Application, Excel.Application. Но приложение может и само являться корневым объектом — например, MS Outlook. Несмотря на это, правила для всех одинаковы, и в любой объект Application встраиваются все остальные объекты-участники, которые являются свойствами главного объекта. У участников могут быть свои участники и так далее. В документе же методов очень много, они самые разные, но есть и аналогичные методы в различных приложениях Office. Навскидку, без размышлений приходят общеизвестные методы Run, Quit, Connect, Activate. Эти методы являются именно аналогичными, но никак не одинаковыми — в разных приложениях они вызываются с различными наборами параметров, да и выполняют часто неадекватные действия. Подобный "разброд" реализаций также представляет из себя одну из причин, почему программисты неохотно используют серверы и контроллеры автоматизации в своих приложениях. Однако, используя доступные теперь компоненты, работать стало намного проще и удобнее. Кроме того, достаточно много информации, хоть и в неудобоваримом виде, можно почерпнуть из файлов справки MS Office, а также MSDN.
Как только открывается документ или создается новый, будь это сделано в MS Excel, MS Word или MS PowerPoint, автоматически сервер создает скелет нового документа, который представляет собой набор классов. Объекты этих классов будут доступны в данном документе. Тогда задачей программиста является получить доступ к корневому объекту сервера, а от него, в свою очередь, ко всем необходимым для работы наследникам, а также правильно передать параметры соответствующим методам.
Организация доступа к объекту Application из Delphi. Но хватит теории. Часть, посвященная описанию MS Office, и так превысила все мыслимые размеры. Теперь, как говорится, попробуем Delphi...
Сначала проверим, способна ли Дельфи подключиться к серверу автоматизации и получить доступ к объекту Application.
Создаем новый проект, на пустую форму бросаем компонент WordApplication с палитры Servers и устанавливаем свойства AutoConnect и AutoQuit в значение True. После этого запускаем приложение. Ничего не случилось. И не должно было. Наше приложение ничего не делает с сервером автоматизации и никаких данных не обрабатывает. Если заглянуть в Task Manager, то можно найти процесс WINWORD.EXE, который, как ни странно, нигде не отображает окна MS Word. Это, собственно, и есть СОМ-сервер приложения MS Word. Если же мы сейчас закроем приложение, то пропадет из списка задач и сервер автоматизации. Учитывая наши вышеупомянутые два свойства, установленные в True, сервер автоматизации автоматически запускается при старте приложения и автоматически завершает свою работу при его выгрузке из памяти.
Как работает этот механизм? Тут надо немного отвлечься и окунуться в дебри интерфейсов. В каждом из файлов описаний (unit) СОМ-серверов вы, наверное, замечали строку типа LIBID_Word: TGUID = '{00020905-0000-0000-C000-000000000046}';. Используя именно этот идентификатор, операционная система просматривает раздел реестра HKCR\CLSID\ на предмет подобной записи GUID в ключе TypeLib. По этому же ключу было найдено зарегистрированное приложение в операционной системе — эту информацию можно извлечь из ключа ProgID того же раздела. Как и ожидалось, значение этого ключа — Word.Document.8. Теперь ОС знает, кому нужен сервер автоматизации (вызвавшему его приложению) и какой конкретно сервер автоматизации затребовало приложение. Далее сервер автоматизации предоставил нашему приложению-контроллеру автоматизации интерфейс для получения доступа к объекту Application. Но все это верно в том случае, если ваш контроллер автоматизации один. А если таких приложений несколько? Что тогда делает сервер автоматизации? Я не буду глубоко вдаваться в подробности интерфейса IUnknown, скажу только, что у этого интерфейса есть три метода, один из которых — _AddRef, отвечает за подсчет количества подключений к серверу. Как только количество активных подключений нулевое, сервер автоматически выгружает себя из памяти компьютера.
Как перестать мучаться и начать программировать;-)? Перед тем, как перейти к практической части, позволю себе маленькое лирическое отступление. Если не брать в расчет тот факт, что для работы вам будет необходима среда разработки Delphi 5 и MS Office в составе MS Word, MS Excel, MS Outlook, вам понадобится еще несколько мелочей. Не забудьте при установке пакета указать инсталлятору о необходимости установки справки по Visual Basic'у — это весьма существенное замечание, и сейчас я объясню почему. Скажите, как вы думаете, откуда я черпаю информацию о том, какие функции и процедуры надо использовать, какие параметры им надо передавать? Правильно, ответ напрашивается сам собой. Выполнение некоторых процессов в документе сродни работе макроса, написанного на Visual Basic, да и названия свойств в файле-оболочке (который генерирует Delphi) почти полностью совпадают с названиями функций в Visual Basic. То есть, нет лучше способа ускорить и облегчить процесс создания программы, чем исследование макросов и справочной информации по командам.
Формирование отчета в MS Word. Теперь приступим непосредственно к практической части и создадим маленький документ Word, чтобы показать основы работы с этим сервером автоматизации. Как известно, основой всех отчетов являются таблицы. Естественно, как главный козырь этого способа положим именно создание таблицы в новом, только что созданном документе.
Но перед созданием нового приложения в Delphi проделаем нехитрые манипуляции с библиотеками типов, которые установлены вместе с пакетом MS Office и Borland Delphi 5. Зачем это нужно, вы скоро поймете. Для начала удалим всю закладку Servers. Делается это так. Следуем по меню: Component / Install Packages / Borland Sample Automation Servers Component / нажимаем Remove. Закладка Servers пропала. Сделано это было для того, чтобы потом загрузить новые библиотеки типов в среду разработки, а значит, получить последнюю версию всей информации о сервере автоматизации и о его возможностях. Теперь нам надо найти библиотеки типов MS Word и MS Excel. Снова следуем по меню Project / Type Library. Из списка серверов выбираем что-то одно, например Microsoft Excel 9.0 Object Library. Хочу заметить, что данная версия библиотеки будет показана только у тех, у кого установлен MS Office версии 2000. В другом случае будет указан другой номер версии библиотеки. Указываем имя закладки компонент — Servers. Выставляем флажок в поле General Component Wraper — это необходимо для того, чтобы, как я говорил выше, Дельфи создала файл-оболочку. Впоследствии это поможет при написании программы, так как после точки в редакторе кода как обычно выскочит список-подсказка с доступными свойствами и методами класса. Все. Нажимаем кнопку Install. Имя пакета лучше выбирать звучное, так сказать, чтобы потом было понятно, что это за файл и к чему он относится. Так, я назвал его d5excel2k.dpk. То же самое необходимо проделать и с другими серверами автоматизации.
Итак, сервер автоматизации доступен, Дельфи 5 запущена. Начнем. Помещаем на форму компонент WordApplication и кнопку. Написать на ней можно что хотите. Далее, свойство AutoConnect установите в true, а свойство AutoQuit — в false, так как закрываться сервер автоматизации у нас будет не автоматически, а программно. Ну, надо же хоть в чем-то отойти от стандартных шаблонов — пусть даже в малом. На этом создание сногсшибательного интерфейса подошло к концу. Результаты можно наблюдать на рисунке 1.

В обработчике OnClick единственной нашей кнопки пропишем следующие строки (пояснения можно опустить;-)):

procedure TDenverWordDemoForm.WordButtonClick (Sender: TObject);
// ну вот так я решил назвать компоненты
var SaveChanges,OriginalFormat,RouteDocument: OleVariant;
Count,Unit_,AutoFitBehavior,DefaultTableBehavior,FileName,FileFormat: OleVariant;
i: integer;
// переменные типа OleVariant необходимы для работы процедур и функций. Я не менял их названий, то есть как назывался параметр для функции, так я и назвал переменную — это для ясности при программировании.
begin
WordApp.Application.Documents.Add(EmptyParam,EmptyParam,EmptyParam,EmptyParam);
// создали новый документ в открывшемся приложении
WordApp.Selection.Font.Bold:= wdToggle;
// переключились на шрифт "жирный"
WordApp.Application.Keyboard(1049);
// здорово, да? Переключились на русскую раскладку клавиатуры
WordApp.Selection.TypeText('Оглавление.');
// начинаем ввод текста
WordApp.Selection.Font.Bold:= wdToggle;
// возвращаем шрифт в нормальное состояние
WordApp.Selection.TypeParagraph; // программно жмем клавишу "Enter"
WordApp.Selection.TypeParagraph;
WordApp.Selection.TypeText('1. Раздел №1.');
WordApp.Selection.TypeParagraph;
WordApp.Selection.TypeText('2. Раздел №2.');
WordApp.Selection.TypeParagraph;
WordApp.Selection.TypeText('3. Раздел №3.');
WordApp.Selection.TypeParagraph;
// начинаем создание таблицы
DefaultTableBehavior:=wdWord9TableBehavior; // устанавливаем тип таблицы — обыкновенный тип для MS Word 2000
AutoFitBehavior:=wdAutoFitFixed; // чтобы таблица была растянута по ширине на весь документ
Unit_:=wdCell; // эта переменная указывает, куда серверу автоматизации переместить курсор при вызове соответствующего метода
Count:=1; // переменная, указывающая, на сколько позиций переместить курсор
WordApp.ActiveDocument.Tables.Add(WordApp.Selection.Range,4,2,DefaultTableBehavior, AutoFitBehavior); // создание таблицы размером 4 строки на 2 столбца
for i:=1 to 8 do // легкое заполнение ячеек таблицы
begin
WordApp.Selection.TypeText(IntToStr(i));
If i <> 8 then
WordApp.Selection.MoveRight(Unit_,Count,EmptyParam); // если ячейка последняя — то не переводить курсор далее, так как тогда будет вставлена новая строка в таблицу
end;
Unit_:=wdParagraph; // переменная содержит значение типа перехода курсора (в данном случае это новый параграф, то есть начать с новой строки ПОСЛЕ таблицы)
WordApp.Selection.MoveDown(Unit_,Count,EmptyParam); // сдвигаем курсор на одну позицию вниз — выходим за рамки таблицы и попадаем в свободное место под таблицей.
WordApp.Selection.TypeText('Просто текст.'); // пишем после таблицы, чтобы удостовериться, что выход из таблицы был сделан правильно
WordApp.Selection.TypeParagraph; // снова перевод каретки
FileName:='test.rtf';
FileFormat:=wdFormatRTF;
SaveChanges:=wdSaveChanges;
OriginalFormat:=wdWordDocument;
RouteDocument:=0;
WordApp.Application.Activedocument.SaveAs(FileName,FileFormat,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam);// собственно, сохраняем документ под именем FileName в формате RTF с изменениями
WordApp.Application.Quit(SaveChanges,originalFormat,RouteDocument); // закрываем сервер автоматизации и уничтожаем его.
end; // вот и все!

Замечу напоследок, что переменная EmptyParam, мистически вставляемая мной во все мыслимые и немыслимые места, является просто заглушкой и служит как раз для того, чтобы сервер автоматизации использовал при исполнении команд значения параметров по умолчанию, так как сама эта переменная ничего из себя не представляет.
Что ж. Вот, уже что-то получилось. Документ создается, в него записывается информация и сохраняется на диске. Кстати, если кто не нашел документа — он в папке "Мои Документы", если, конечно, вы не прописывали полный путь для создаваемого файла.
Как вы поняли, это еще не все. Во второй части статьи обеспечим работу нашего приложения с MS Excel 2000 и MS Outlook. Всем удачи в программировании, денег, счастья, снова денег, здоровья — и так по нарастающей!

Денис Мигачев АКА Denver
denver@neman.grodno.by



© Компьютерная газета

полезные ссылки
Корпусные камеры видеонаблюдения