Delphi и OLE Automation с Word
Автоматизация позволяет одному приложению управлять другим приложением. Управляемое приложение называется сервером автоматизации (в нашем случае Word). Приложение, управляющее сервером называется диспетчером автоматизации.
Есть два пути для получения доступа к серверам автоматизации:
Позднее связывание (Интерфейс IDispatch)
При использовании данного метода имена функций и типы параметров решаются во время выполнения программы, все параметры определены вариантным типом.
Поскольку во время компиляции невозможно определить соответствия имен функций и типов параметров, данный метод чреват ошибками.
Так как имена функций и типы параметров должны проверяться во время выполнения программы, данный метод выполняется медленно.
Единственное преимущество данного метода при программировании в Delphi заключается в том, что отпадает необходимость передачи всех параметров вызываемой функции.
Раннее связывание (Использование библиотеки типов/интерфейсов)
При использовании данного метода имена функций и типы параметров полностью решаются во время компиляции.
Библиотека типов должна импортироваться в Delphi. Библиотека типов является языковым нейтральным описанием всех объектов и функций, поддерживаемых сервером. (Это подобно файлу заголовка языка C).
При вызове функции должны обязательно присутствовать все параметры, даже те, которые в документации указаны как дополнительные (необязательные). Это позволяет обнаружить и исправить множество ошибок еще до запуска программы.
Скорость выполнения значительно быстрее, чем при использовании позднего связывания.
Из-за преимуществ второго метода остальная часть документа демонстрирует принципы создания приложений с ранним связыванием. Все приложения, использующие Excel автоматизацию, должны пользоваться последним методом, если нет причин для первого.
Подготовка библиотеки типов.
Модуль Pascal должен быть создан на основе файла библиотеки типов.
- Выберите пункт меню Project|Import Type Library
- Нажмите кнопку Add и выберите следующий файл
- c:\program files\microsoft office\office\msword8.olb
- Нажмите OK.
К сожалению, данный модуль с проектом явно не компилируется, хотя и включается в него, вероятно из-за того, что приложение считает данный модуль нечто вроде текстового приложения.
Наиболее простой путь заключается в следующем: удалите модуль excel_tlb из проекта и только после этого добавьте его в список используемых модулей.
Документация
Справочный файл c:\program files\microsoft office\office\vbawrd8.hlp содержит информацию о доступных объектах Word.
"Записыватель" макросов позволяет быстро создавать VBA-код. После этого он довольно может легко быть портирован в Delphi.
Пример автоматизации
Следующий пример использует класс-оболочку Delphi, инкапсулирующий прямые вызовы объектов Word. Вот преимущество этого метода:
- Обеспечение скрытия параметров. Возможность использования для многих методов параметров по умолчанию. Многие методы Word также работают с вариантными параметрами. Это означает невозможность использования констант - скрытие параметров решает данную проблему.
- Обеспечение проверки типа. Многие методы определены с параметрами OLEVariant, обеспечивая внешнюю совместимость.
- Следующий класс-оболочка демонстрирует ключевые методы автоматизации Word. Полностью класс приведен в Приложении 1.
unit doc; interface uses windows, sysutils, Word_TLB;type TWinWord = classPrivateApp : _Application;publicconstructor Create;destructor Destroy; override;procedure NewDoc(Template : String);procedure GotoBookmark(Bookmark : String);procedure InsertText(Text : String);procedure SaveAs(Filename : string);end; //------------------------------------------------------------------ implementation //------------------------------------------------------------------ constructor TWinWord.Create; begin App := CoApplication.Create;end; //------------------------------------------------------------------ destructor TWinWord.Destroy; varSaveChanges : OLEVariant;OriginalFormat : OLEVariant;RouteDocument : OLEVariant;begin SaveChanges := wdDoNotSaveChanges;OriginalFormat := unAssigned;RouteDocument := unAssigned;app.Quit(SaveChanges, OriginalFormat, RouteDocument);inherited destroy;end; //------------------------------------------------------------------ procedure TWinWord.GotoBookmark(Bookmark : string); var What : OLEVariant;Which : OLEVariant;Count : OLEVariant;Name : OLEVariant;begin What := wdGoToBookmark;Which := unAssigned;Count := unAssigned;Name := Bookmark;App.Selection.GoTo_(What, Which, Count, Name);end; //------------------------------------------------------------------ procedure TWinWord.InsertText(Text : String); begin App.Selection.TypeText(Text);end; //------------------------------------------------------------------ procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant;NewTemplate : OleVariant;begin DocTemplate := Template;NewTemplate := False;App.Documents.Add(DocTemplate, NewTemplate);end; //------------------------------------------------------------------ procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName);end; //------------------------------------------------------------------ end. |
Добавьте модуль библиотеки типов в список используемых модулей.
uses windows, sysutils, Word_TLB; |
Создадим определение класса:
TWinWord = class PrivateApp : _Application;publicprocedure NewDoc(Template : String);procedure GotoBookmark(Bookmark : String);procedure InsertText(Text : String);procedure SaveAs(Filename : string);constructor Create;destructor Destroy; override;end; |
Опубликованные (public) процедуры - процедуры, которые могут быть использованы при работе с классом.
Создадим конструктор.
constructor TWinWord.Create; begin App := CoApplication.Create;end; |
Реализация дектруктора
destructor TWinWord.Destroy; var SaveChanges : OLEVariant;OriginalFormat : OLEVariant;RouteDocument : OLEVariant;begin SaveChanges := wdDoNotSaveChanges;OriginalFormat := unAssigned;RouteDocument := unAssigned;app.Quit(SaveChanges, OriginalFormat, RouteDocument);inherited destroy;end; |
Реализуем метод NewDoc. Этот метод создаст новый текстовый документ на основе заданного шаблона.
procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant;NewTemplate : OleVariant;begin DocTemplate := Template;NewTemplate := False;App.Documents.Add(DocTemplate, NewTemplate);end; |
Ниже показана реализация метода SaveAs. Данный метод позволит сохранить в файле текущий документ.
procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName);end; |
Данный пример использует позднее связывание. В результате имя метода и используемые типы проверяются только во время прогона. Изменение имени метода в этом случае не вызовет ошибку во время компиляции. В нашем случае технология позднего связывания более эффективна, так как метод SaveAs требует 12 параметров, но только параметр FileName является обязательным. Так как данный вызов можно считать единичным, его выполнение не может особо сказаться на скорости выполнения программы.
Следующий код демонстрирует использование данного класса для создания, редактирования, печати и сохранения документа, созданного на основе шаблона:
Word := TWinWord.create; try Word.visible := true;Word.NewDoc('c:\delphi\word\sample\Demo');Word.GotoBookmark('From');Word.InsertText('Иван Иваныч');Word.GotoBookmark('Dept');Word.InsertText('Разработка');Word.GotoBookmark('Phone');Word.InsertText('111111');Word.GotoBookmark('Now');Word.InsertText(FormatDateTime('d-mmm-yyyy', now));//SF элементыWord.GotoBookmark('Items');Word.InsertText('112021');Word.MoveRight(1);Word.InsertText('PVCS');Word.MoveRight(1);Word.InsertText('1');Word.MoveRight(1);Word.InsertText('Ј 305.99');Word.MoveRight(1);Word.InsertText('Ј 305.99');Word.MoveRight(1);Word.UpdateFields;Word.RunMacro('Demo');Word.Print;Word.SaveAs(filename);finally Word.Free;end; |
Итог
- Всегда используйте раннее связывание.
- Если позднее связывание необходимо для вызовов некоторых функций, используйте где возможно раннее связывание и преобразование типа объектной переменной к типу OLEVariant для вызовов, требующим позднее связывание.
- Не включайте модуль библиотеки типов в ваш проект. Добавьте его только в список используемых модулей.
- Создавайте код автоматизации в отдельном модуле. Инкапсулируйте вызовы в классе-оболочке.
- Используйте "записыватель" макросов Word для создания прототипа кода автоматизации.
- Используйте файл электронной справки vbawrd8.hlp для получения информации об объектах Word.
- Используйте модуль Word_tlb.pas для проверки необходимых Delphi типов и количества параметров. Для проверки правильности кода перекомпилируйте проект, нажав клавиши <CTRL><F9>.
- Загружайте и используйте шаблоны Word, содержащие предварительное форматирование текста. Этот способ существенно быстрее и не требует большого времени для создания сложноформатированных документов. Шаблоны ДОЛЖНЫ сохраняться приложением в своей рабочей директории. Это поможет избежать проблем, связанных с конфликтом имен.
- Используйте закладки (Bookmarks) для определения области ввода текста приложением Delphi.
- Удостоверьтесь в том, что ваш код содержит команду закрытия приложения Word (app.quit). Не вызывая app.quit, можно быстро исчерпать системные ресурсы, особенно при работе с большим количеством документов Word. Обратите на это особое внимание.
- Наличие множества незакрытых документов Word легко проверить в Windows NT, используя Менеджер Задач (нажмите CTL+ALT+Del для его открытия).
Полный исходный код класса tWinWord приведен ниже. Он включает реализацию всех методов:
unit doc; interface uses Word_TLB, windows, sysutils;Type TWinWord = class PrivateApp : _Application;function fGetVisible : boolean;procedure fSetVisible(visible : boolean);publicprocedure NewDoc(Template : String);procedure GotoBookmark(Bookmark : String);procedure InsertText(Text : String);procedure MoveRight(Count : integer);procedure Print;procedure UpdateFields;procedure SaveAs(Filename : string);Procedure RunMacro(MacroName : string);constructor Create;destructor Destroy; override;property visible : booleanread fGetVisiblewrite fSetVisible;end; implementation //------------------------------------------------------------------ constructor TWinWord.Create; begin App := CoApplication.Create;end; //------------------------------------------------------------------ destructor TWinWord.Destroy; varSaveChanges : OLEVariant;OriginalFormat : OLEVariant;RouteDocument : OLEVariant;begin SaveChanges := wdDoNotSaveChanges;OriginalFormat := unAssigned;RouteDocument := unAssigned;app.Quit(SaveChanges, OriginalFormat, RouteDocument);inherited destroy;end; //------------------------------------------------------------------ function TWinWord.fGetVisible : boolean; begin result := App.Visible;end; //------------------------------------------------------------------ procedure TWinWord.fSetVisible(Visible : boolean); begin App.visible := Visible;end; //------------------------------------------------------------------ procedure TWinWord.GotoBookmark(Bookmark : string); var What : OLEVariant;Which : OLEVariant;Count : OLEVariant;Name : OLEVariant;begin What := wdGoToBookmark;Which := unAssigned;Count := unAssigned;Name := Bookmark;App.Selection.GoTo_(What, Which, Count, Name);end; //------------------------------------------------------------------ procedure TWinWord.InsertText(Text : String); begin App.Selection.TypeText(Text);end; //------------------------------------------------------------------ procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant;NewTemplate : OleVariant;begin DocTemplate := Template;NewTemplate := False;App.Documents.Add(DocTemplate, NewTemplate);end; //------------------------------------------------------------------ procedure TWinWord.MoveRight(Count : integer); var MoveUnit : OleVariant;vCount : OleVariant;Extended : OleVariant;begin MoveUnit := wdCell;vCount := Count;Extended := unassigned;app.selection.MoveRight(MoveUnit, vCount, Extended);end; //------------------------------------------------------------------ procedure TWinWord.Print; begin OLEVariant(app).Printout;end; //------------------------------------------------------------------ procedure TWinWord.UpdateFields; begin App.ActiveDocument.Fields.Update;end; //------------------------------------------------------------------ procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName);end; //------------------------------------------------------------------ procedure TWinWord.RunMacro(MacroName : string); begin App.Run(MacroName);end; //------------------------------------------------------------------ end. |