Delphi и OLE Automation с Word
|
In the beginning was the Word, and the Word was 1.0...
|
Автоматизация позволяет одному приложению управлять другим
приложением. Управляемое приложение называется сервером автоматизации (в нашем
случае 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 = class
private
App: _Application;
public
constructor 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;
var
SaveChanges: 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
Private
App : _Application;
public
procedure NewDoc(Template : String);
procedure GotoBookmark(Bookmark : String);
procedure InsertText(Text : String);
procedure SaveAs(Filename : string);
constructor Create;
destructor Destroy; override;
end;
|
Переменная App является ссылкой на приложение Word. Это допускает вызов
методов Word с применением технологии раннего связывания.
Опубликованные (public) процедуры - процедуры, которые могут быть
использованы при работе с классом.
Создадим конструктор.
constructor TWinWord.Create;
begin
App := CoApplication.Create;
end;
|
Он вызывается при создании класса TWinWord. CoApplication.create создает
новый экземпляр Word и возвращает ссылку на интерфейс Application. Это позволяет
вызывать методы объекта app.
Реализация дектруктора
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;
|
Деструктор должен вызываться В ОБЯЗАТЕЛЬНОМ ПОРЯДКЕ. Метод Quit объекта
приложения закрывает Word и распределяет всю связанную с ним память. Так как
параметры метода Quit определены как вариантный тип OLEVariant, вся свазанная с
ними память распределяется именно для этого типа переменных.
Реализуем метод NewDoc. Этот метод создаст новый текстовый документ на основе
заданного шаблона.
procedure TWinWord.NewDoc(Template : String);
var
DocTemplate : OleVariant;
NewTemplate : OleVariant;
begin
DocTemplate := Template;
NewTemplate := False;
App.Documents.Add(DocTemplate, NewTemplate);
end;
|
Данный метод осуществляет более строгую проверку типов, чем это осуществляет
сам Word. Параметр Template должен содержать строку с именем шаблона. Метод Word
Add в качестве параметров может содержать значения любого типа. Лучшая пример
этого - метод MoveRight, реализация которого показана в Приложении 1.
Ниже показана реализация метода 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 для его открытия).
Приложение A – Полный исходный код сласса TWinWord
Полный исходный код класса tWinWord приведен ниже. Он включает реализацию
всех методов:
unit doc;
interface
uses
Word_TLB, windows, sysutils;
type
TWinWord = class
private
App: _Application;
function fGetVisible: boolean;
procedure fSetVisible(visible: boolean);
public
procedure 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: boolean
read fGetVisible
write fSetVisible;
end;
implementation
//------------------------------------------------------------------
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;
//------------------------------------------------------------------
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.
|
|