Часть 1. Microsoft Word и Microsoft Excel
Коротко об автоматизации
VBA и средства разработки контроллеров автоматизации
Объектные модели Microsoft Office
Общие принципы создания контроллеров автоматизации
Автоматизация Microsoft Word
Программные идентификаторы и объектная модель Microsoft Word
Создание и открытие документов Microsoft Word
Сохранение, печать и закрытие документов Microsoft Word
Вставка текста и объектовв документ и форматирование текста
Перемещение курсора по тексту
Создание таблиц
Обращение к свойствам документа
Автоматизация Microsoft Excel
Программные идентификаторы и объектная модель Microsoft Excel
Запуск Microsoft Excel, создание и открытие рабочих книг
Сохранение, печать и закрытие рабочих книг Microsoft Excel
Обращение к листам и ячейкам
Создание
диаграмм
Настоящая статья посвящена теме, может быть, и не новой,
но, как показывают письма читателей, по-прежнему актуальной — автоматизации
приложений Microsoft Office. Многие разработчики в процессе работы над своими
проектами (неважно, с помощью какого средства разработки — Delphi, C++Builder,
Visual Basic…) нередко применяют сервисы, предоставляемые Microsoft Office,
например построение сводных таблиц и диаграмм с помощью Microsoft Excel, генерацию
и печать документов с помощью Microsoft Word и т.д. Нередко пользователи, привыкшие
использовать приложения Microsoft Office в повседневной работе, сами настаивают
на применении в приложениях таких сервисов либо просто на сохранении отчетов
и других документов в одном из форматов Microsoft Office. Отметим, что потенциальные пожелания подобного рода
компанией Microsoft учтены достаточно давно — практически все, что в состоянии
сделать пользователь любого приложения Microsoft Office с помощью меню, клавиатуры
и инструментальной панели, может быть произведено и автоматически, то есть либо
из VBA-программы, либо из приложения, созданного с помощью одного из средств
разработки. Иными словами, приложения Microsoft Office являются программируемыми.
Программируемость в общем случае означает возможность
управления данным приложением с помощью макроязыков либо с помощью других приложений.
Все компоненты Microsoft Office поддерживают один и тот же макроязык: Visual
Basic for Applications (VBA), позволяющий создавать приложения непосредственно
внутри документов Office (это называется «решения на базе Microsoft Office»).
Управление же компонентами Office из других приложений осуществляется с помощью
автоматизации (Automation, ранее — OLE Automation) — все приложения
Microsoft Office являются серверами автоматизации (или COM-серверами).
Для создания таких приложений пригодны любые средства
разработки, позволяющие создавать контроллеры автоматизации (COM-клиенты).
Наиболее часто для этой цели используется Visual Basic,
но это могут быть и Delphi, и C++Builder, и Visual C++. Однако прежде чем обсуждать
возможности тех или иных средств разработки, следует разобраться, что такое
автоматизация.
Коротко об автоматизации
Автоматизация — это одна из возможностей, предоставляемых технологией
Microsoft COM (Component Object Model). Не вдаваясь в подробности реализации
этой технологии, заметим, что она используется приложениями (называемыми COM-серверами)
для предоставления доступа к их объектам, а также к свойствам и методам этих
объектов другим приложениям (называемым COM-клиентами), каковыми могут быть
и средства разработки. Например, текстовый процессор, будучи COM-сервером, может
предоставлять другим приложениям доступ к документу, абзацу, закладке с помощью
соответствующих объектов. Для именования (и опознания) COM-серверов обычно используются
специальные строковые обозначения — программные идентификаторы (Programmatic
Identifier, ProgID). Они нужны для того, чтобы операционная система могла с
помощью этих идентификаторов определить, в каком именно каталоге (или на каком
компьютере локальной сети, если используется тот или иной способ удаленного
доступа к серверу) расположен исполняемый файл сервера автоматизации, и запустить
его на выполнение. О том, какие именно программные идентификаторы применяются
для того или иного приложения Microsoft Office, будет сказано в разделах, посвященных
этим приложениям.
Объекты автоматизации с точки зрения программирования мало чем отличаются от
обычных объектов, знакомых нам из теории и практики объектно-ориентированного
программирования. Как и обычные объекты, они обладают свойствами и методами.
Свойство — это характеристика объекта; например, свойством абзаца (объект
Paragraph) может быть его стиль (Style). Методом называется действие, которое
можно выполнить с объектом (например, можно сохранить документ с помощью метода
SaveAs объекта Document).
Нередко серверы автоматизации содержат наборы однотипных объектов, называемых
коллекциями. Например, текстовый процессор может содержать коллекцию документов,
а каждый документ — коллекцию абзацев.
Как узнать, какие объекты доступны в серверах автоматизации? Для этой цели
в комплект поставки любого сервера автоматизации входит документация и файлы
справки, описывающие их объектную модель. Если брать приложения Microsoft Office
2000, то это справочные файлы для программистов на Visual Basic for Applications
VBAxxx9.CHM (для Microsoft Office 97 — VBxxx8.HLP соответственно). Отметим,
что по умолчанию они не устанавливаются, так как нужны разработчикам, а не рядовым
пользователям.
Вся информация об объектах, необходимая контроллерам автоматизации, содержится
в библиотеках типов. Библиотеки типов хранятся в специальном бинарном формате
и обычно представлены в виде файлов с расширениями *.olb или *.tlb, а также
могут содержаться внутри исполняемых файлов (*.exe) или динамически загружаемых
библиотек (*.dll). Библиотеки типов можно просматривать с помощью утилиты OleView,
входящей в состав Microsoft Platform SDK, а средства разработки фирмы Borland
содержат свои собственные утилиты просмотра библиотек типов.
Заканчивая это более чем краткое введение в автоматизацию, остановимся также
на понятии связывания. Связыванием называется способ, с помощью которого переменная
или класс приложения-контроллера ссылается на объект автоматизации. Таких способов
существуют два: позднее связывание и раннее связывание.
Позднее связывание означает, что реальная ссылка на объект в переменной или
классе появляется на этапе выполнения приложения-контроллера. Этот способ обычно
более медленный, чем ранее связывание, но он доступен во всех средствах разработки,
позволяющих создавать контроллеры автоматизации, и менее чувствителен к тому,
все ли параметры методов перечислены при их вызовах в коде приложения-контроллера.
При позднем связывании корректность вызовов методов проверяется в момент их
осуществления, то есть на этапе выполнения приложения, а не на этапе его компиляции.
Раннее связывание означает, что реальная ссылка на объект автоматизации появляется
уже на этапе компиляции. Поэтому в коде приложения-контроллера требуется ссылка
на библиотеку типов (или на специальный модуль, являющийся интерфейсом к ней),
и это приводит к генерации в этом приложении классов с теми же свойствами и
методами, что и у объектов автоматизации, что делает возможными синтаксическую
проверку имен методов на этапе компиляции и доступ к именованным константам,
содержащимся в библиотеке типов. Отметим, однако, что далеко не все средства
разработки, поддерживающие позднее связывание, осуществляют также поддержку
раннего связывания.
Закончив экскурс в COM и автоматизацию, вернемся к вопросу о том, какие средства
разработки удобно применять для создания контроллеров автоматизации.
|
VBA и средства разработки контроллеров автоматизации
Как уже было сказано выше, контроллеры автоматизации можно создавать с помощью
различных средств разработки, и если вы уже используете какое-либо из инструментальных
средств, поддерживающих создание контролеров автоматизации, можно именно им
и воспользоваться. Единственное, что следует сделать — это узнать, поддерживается
ли вашим средством разработки ранее связывание, если вы планируете его использовать.
Примеры простейших контроллеров автоматизации (как правило, для Microsoft Word
или Microsoft Excel), дающие представление о том, как вызываются методы сервера,
обычно можно найти в комплекте поставки большинства современных средств разработки.
Освоение объектной модели автоматизируемого сервера можно начать с записи необходимой
последовательности действий в виде макроса с помощью VBА. Создать макрос можно,
выбрав из меню приложения Microsoft Office пункт Tools | Macro |Record New Macro.
Просмотр полученного макроса в редакторе кода VBA обычно позволяет понять, как
должен выглядеть код, реализующий эту последовательность действий.
Обсудив возможные средства разработки контроллеров автоматизации, можно наконец
перейти к самим приложениям Microsoft Office, к их объектным моделям и к их
использованию. В качестве средства разработки для приведенных ниже примеров
используется Borland Delphi 5, но поскольку в них приводится последовательность
вызова свойств и методов объектов Microsoft Office, перенос кода на другие языки
программирования не должен вызвать особых затруднений. Если специально не оговорено,
в данной статье во всех примерах используется раннее связывание.
|
Объектные модели Microsoft Office
Как было сказано выше, приложения Microsoft Office предоставляют контроллерам
автоматизации доступ к своей функциональности с помощью своей объектной модели,
представляющей собой иерархию объектов. Объекты могут предоставлять доступ к
другим объектам посредством коллекций.
В качестве иллюстрации того, как выглядит иерархия объектов Microsoft Office,
приведем небольшой фрагмент объектной модели Microsoft Word (рис.1):
В объектных моделях всех приложений Microsoft Office
всегда имеется самый главный объект, доступный приложению-контроллеру и представляющий
само автоматизируемое приложение. Для всех приложений Microsoft Office он называется
Application, и многие его свойства и методы для всех этих приложений также одинаковы.
Наиболее часто мы будем использовать следующие из них:
- Свойство Visible (доступное для объекта Application всех приложений Microsoft
Office) позволяет приложению появиться на экране и в панели задач; оно принимает
значения True (пользовательский интерфейс приложения доступен) или False (пользовательский
интерфейс приложения недоступен; это значение устанавливается по умолчанию).
Если вам нужно сделать что-то с документом Office в фоновом режиме, не информируя
об этом пользователя, можно не обращаться к этому свойству — в этом случае
приложение можно будет найти только в списке процессов с помощью приложения
Task Manager. - Метод Quit закрывает приложение Office. В зависимости от того, какое приложение
Office автоматизируется, он может иметь параметры или не иметь таковых.
|
Общие принципы создания контроллеров автоматизации
В общем случае контроллер автоматизации должен выполнять следующие действия:
- Проверить, запущена ли копия приложения-сервера.
- В зависимости от результатов проверки запустить копию автоматизируемого
приложения Office либо подключиться к уже имеющейся копии. - Сделать окно приложения-сервера видимым (в общем случае это не обязательно).
- Выполнить какие-то действия с приложением-сервером (например, создать или
открыть документы, изменить их данные, сохранить документы и пр.) - Закрыть приложение-сервер, если его копия была запущена данным контроллером,
или отключиться от него, если контроллер подключился к уже имеющейся копии.
Соответствующий код для Delphi представлен в листинге 1.
Здесь мы воспользовались функциями GetActiveOleObject и CreateOleObject для
подключения к уже запущенной копии приложения-сервера или запуска новой, если
сервер не запущен, что приводит к тому, что в вариантную переменную помещается
ссылка на объект Application соответствующего сервера.
Приведенная часть кода контроллера в других средствах разработки может выглядеть
по-другому — она не имеет прямого отношения к методам объектов Office,
так как обусловлена правилами вызова стандартных функций OLE в Delphi (за исключением
вызовов методов Visible и Quit объекта Application). А вот все то, что должно
быть вставлено вместо комментария «Здесь выполняются другие действия с объектами
приложения Office», в разных средствах разработки должно выглядеть более или
менее однотипно — используя созданную вариантную переменную, мы манипулируем
методами и свойствами объекта Application.
В заключение сделаем одно маленькое замечание касательно числа параметров методов
объектов автоматизации. В случае применения позднего связывания число указанных
в коде параметров метода не обязано совпадать с их истинным числом (которое
можно найти в описании объектной модели соответствующего приложения). В этом
случае вполне допустимым является следующий код:
App.Quit;
несмотря на то что метод Quit объекта Application в случае некоторых приложений
Microsoft Office (например, Microsoft Word) имеет параметры.
При использовании же раннего связывания, например при создании контроллеров
с помощью Delphi, следует более строго подходить к определению параметров —
их число и тип должны соответствовать описанию методов в библиотеке типов. Например,
в случае раннего связывания корректный код на Delphi для закрытия документа
Word со значениями всех параметров по умолчанию будет иметь вид:
App.Quit(EmptyParam, EmptyParam, EmptyParam);
Исключением из этого правила является Visual Basic — это средство разработки
позволяет не задумываться о числе параметров методов при написании кода.
Обсудив общие принципы создания контроллеров автоматизации и узнав, как это
делается средствами Borland Delphi, мы можем перейти к рассмотрению автоматизации
конкретных приложений Microsoft Office. Начнем с одного из самого популярных
компонентов этого пакета — с Microsoft Word.
|
Автоматизация Microsoft Word
В данном разделе мы обсудим наиболее часто встречающиеся задачи, связанные
с автоматизацией Microsoft Word. Но перед этим рассмотрим, каковы программные
идентификаторы основных объектов Microsoft Word и что представляет собой его
объектная модель.
|
Программные идентификаторы и объектная модель Microsoft Word
Для приложения-контроллера доступны непосредственно следующие объекты:
Объект |
Программный идентификатор |
Комментарий |
---|---|---|
Application |
Word.Application, Word.Application.9 |
С помощью этого программного идентификатора создается экземпляр Word |
Document |
Word.Document, Word.Document.9, Word.Template.8 |
С помощью этого программного идентификатора создается экземпляр Word |
Все остальные объекты Word являются так называемыми внутренними объектами.
Это означает, что они не могут быть созданы сами по себе; так, объект Paragraph
(абзац) не может быть создан отдельно от содержащего его документа.
Если вспомнить, что основное назначение Word — работа с документами, можно
легко понять иерархию его объектной модели (фрагмент ее был показан на рис.
1). Основным объектом в ней, как иво всех других приложениях Microsoft Office,
является объект Application, содержащий коллекцию Documents объектов типа Document.
Каждый объект типа Document содержит коллекцию Paragraphs объектов типа Paragraph,
Bookmarks типа Bookmark, Characters типа Character и т.д. Манипуляция документами,
абзацами, символами, закладками реально осуществляется путем обращения к свойствам
и методам этих объектов.
Ниже мы рассмотрим наиболее часто встречающиеся задачи, связанные с автоматизацией
Microsoft Word. Если же вам встретилась задача, не совпадающая ни с одной из
перечисленных, вы можете попытаться найти подходящий пример на Visual Basic
в справочном файле VBAWRD9.CHM, либо, как уже было сказано выше, записать необходимую
последовательность действий в виде макроса и проанализировать его код.
|
Создание и открытие документов Microsoft Word
Для создания примеров использования Microsoft Word можно использовать код создания
контроллера, приведенный выше, и модифицировать его, заменяя комментарии кодом,
манипулирующим свойствами и методами объекта Word.Application. Мы начнем с создания
и открытия документов.
Создать новый документ Word можно, используя метод Add коллекции Documents
объекта Application:
App.Documents.Add;
Как создать нестандартный документ? Очень просто — нужно указать имя шаблона
в качестве параметра метода Add:
App.Documents.Add(‘C:Program Files _ Microsoft OfficeTemplates1033Manual.dot’);
Для открытия уже существующего документа следует воспользоваться методом Open
коллекции Documents:
App.Documents.Open(‘C:MyWordFile.doc’);
Отметим, что свойство ActiveDocument объекта Word.Application указывает на
текущий активный документ среди одного или нескольких открытых. Помимо этого
к документу можно обращаться по его порядковому номеру с помощью метода Item;
например ко второму открытому документу можно обратиться так:
App.Documents.Item(2)
Отметим, что нумерация членов коллекций в Microsoft Office начинается с единицы.
Сделать документ активным можно с помощью метода Activate:
App.Documents.Item(1).Activate;
Следующее, чему следует научиться — это сохранять документ Word и закрывать
сам Word.
|
Сохранение, печать и закрытие документов Microsoft Word
Закрытие документа может быть осуществлено с помощью метода Close:
App.Documents.Item(2).Close;
или
App.ActiveDocument.Close ;
Метод Close имеет несколько необязательных (в случае позднего связывания) параметра,
влияющих на правила сохранения документа. Первый из них влияет на то, сохраняются
ли внесенные в документ изменения, и принимает три возможных значения (соответствующие
константы рекомендуется описать в приложении. См. листинг
2):
Третий параметр принимает значения True или False и влияет на то, пересылать
ли документ следующему пользователю по электронной почте. Если эта функциональность
не применяется, можно проигнорировать этот параметр.
Таким образом, при использовании этих параметров закрыть документ можно, например,
так:
App.ActiveDocument.Close(wdSaveChanges, _ wdPromptUser) ;
Просто сохранить документ, не закрывая его, можно с помощью метода Save:
App.ActiveDocument.Save;
Этот метод также имеет несколько необязательных (в случае позднего связывания)
параметров, первый из которых равен True, если документ сохраняется автоматически,
и False, если нужно выводить диалоговую панель для получения подтверждения пользователя
о сохранении изменений (если таковые были сделаны). Второй параметр влияет на
формат сохраняемого документа, и список его возможных значений совпадает со
списком значений второго параметра метода Close.
Напоминаем, что закрыть сам Word можно с помощью метода Quit объекта Word.Application.
Этот метод имеет в общем случае три параметра, совпадающих с параметрами метода
Close объекта Document.
Вывод документа на устройство печати можно осуществить с помощью метода PrintOut
объекта Document, например:
App.ActiveDocument.PrintOut;
Если нужно изменить параметры печати, следует указать значения соответствующих
параметров метода PrintOut (в случае Microsoft Word их около двадцати).
|
Вставка текста и объектовв документ и форматирование текста
Для создания абзацев в документе можно использовать коллекцию Paragraphs объекта
Document, представляющую набор абзацев данного документа. Добавить новый абзац
можно с помощью метода Add этой коллекции:
App.ActiveDocument.Paragraphs.Add;
Для вставки собственно текста в документ, тем не менее, применяется не объект
Paragraph, а объект Range, представляющий любую непрерывную часть документа
(в том числе и вновь созданный абзац). Этот объект может быть создан разными
способами. Например, можно указать начальный и конечный символы диапазона (если
таковые имеются в документе):
var Rng : Variant; ... Rng := App.ActiveDocument.Range(2,4); //со 2-го по 4-й символы
или указать номер абзаца (например, только что созданного):
Rng:= App.ActiveDocument.Paragraphs.Item(1).Range;
или указать несколько абзацев, следующих подряд:
Rng := App.ActiveDocument.Range _ (App.ActiveDocument.Paragraphs.Item(3).Range.Start,_ App.ActiveDocument.Paragraphs.Item(5).Range.End)
Вставить текст можно с помощью методов объекта Range InsertBefore (перед диапазоном)
или InsertAfter (после диапазона), например:
Rng.InsertAfter(‘Это вставляемый текст’);
Помимо объекта Range текст можно вставлять с помощью объекта Selection, являющийся
свойством объекта Word.Application и представляющий собой выделенную часть документа
(этот объект создается, если пользователь выделяет часть документа с помощью
мыши, и может быть также создан с помощью приложения-контроллера). Сам объект
Selection можно создать, применив метод Select к объекту Range, например:
var Sel : Variant; ... App.ActiveDocument.Paragraphs.Item(3).Range.Select;
В приведенном выше примере в текущем документе выделяется третий абзац.
Если мы хотим вставить строку текста в документ либо вместо выделенного фрагмента
текста, либо перед ним, это можно сделать с помощью следующего фрагмента кода:
var Sel : Variant; ... Sel := App.Selection; Sel.TypeText(‘Это текст, которым мы заменим _ выделенный фрагмент’);
Отметим, что если свойство Options.ReplaceSelection объекта Word.Application
равно True, выделенный текст будет заменен на новый текст (этот режим действует
по умолчанию); если же нужно, чтобы текст был вставлен перед выделенным фрагментом,
а не вместо него, следует установить это свойство равным False:
App.Options.ReplaceSelection := False;
Символ конца абзаца при использовании объекта Selection может быть вставлен
с помощью следующего фрагмента кода:
Sel.TypeParagraph;
К объекту Selection, так же как и к объекту Range, можно применить методы InsertBefore
и InsertAfter. В этом случае, в отличие от предыдущего, вставляемый текст станет
частью выделенного фрагмента текста.
С помощью объекта Selection, используя его свойство Font и свойства объекта
Font, такие как Bold, Italic, Size,…, можно отформатировать текст. Например,
таким образом можно вставить строку, выделенную жирным шрифтом:
Sel.Font.Bold := True; Sel.TypeText(‘Это текст, который мы выделим _ жирным шрифтом.’); Sel.Font.Bold := False; Sel.TypeParagraph;
Для наложения на вставляемый текст определенного заранее стиля можно использовать
свойство Style этого же объекта, например:
Sel.Style := ‘Heading 1’; Sel.TypeText(‘Это текст, который станет _ заголовком’); Sel.TypeParagraph;
Нередко документы Word содержат данные других приложений. Простейший способ
вставить такие данные в документ — использовать метод Paste объекта Range:
Var Rng : Variant; ... Rng := App.Selection.Range; Rng.Collapse(wdCollapseEnd); Rng.Paste;
Естественно, в этом случае в буфере обмена уже должны содержаться вставляемые
данные.
Если нужно поместить в буфер обмена часть документа Word, это можно сделать
с помощью метода Copy объекта Range:
Var Rng : Variant; ... Rng := App.Selection.Range; Rng.Copy;
Следующее, чему нужно научиться — перемещению курсора в нужное место текста,
чем мы и займемся в следующем разделе.
|
Перемещение курсора по тексту
Используя метод Collapse, можно «сжать» объект Range или объект Selection,
сократив его размер до нуля символов:
Rng.Collapse(wdCollapseEnd);
Параметр этого метода указывает, в начале или в конце исходного фрагмента окажется
новый объект Range или Selection. Если вы используете позднее связывание и ваше
средство разработки — не Visual Basic, нужно определить в приложении соответствующие
константы:
const wdCollapseStart = $00000001; //новый объект находится в начале фрагмента wdCollapseEnd = $00000000; //новый объект находится в конце фрагмента
Перемещать курсор по тексту можно с помощью метода Move объектов Range и Selection.
Этот метод имеет два параметра. Первый указывает на то, в каких единицах измеряется
перемещение — в символах (по умолчанию), словах, предложениях, абзацах и др.
Второй параметр указывает, на сколько единиц при этом нужно переместиться (это
число может быть и отрицательным; по умолчанию оно равно 1). Например, следующий
фрагмент кода:
Rng.Move;
приведет к перемещению курсора на один символ вперед, а
Rng.Move(wdParagraph,3);
приведет к перемещению курсора на три абзаца вперед. Отметим, что этот метод
использует следующие константы:
const //Единицей перемещения является: wdCharacter = $00000001; //символ wdWord = $00000002; //слово wdSentence = $00000003; //предложение wdParagraph = $00000004; //абзац wdStory = $00000006; //часть документа //напр., колонтитул, //оглавление и др.) wdSection = $00000008; //раздел wdColumn = $00000009; //колонка таблицы wdRow = $0000000A; //строка таблицы wdCell = $0000000C; //ячейка таблицы wdTable = $0000000F; //таблица
Нередко для перемещения по тексту используются закладки. Создать закладку в
текущей позиции курсора можно путем добавления члена коллекции Bookmarks объекта
Document c помощью метода Add, указав имя закладки в качестве параметра, например:
App.ActiveDocument.Bookmarks.Add(‘MyBookmark’);
Проверить существование закладки в документе можно с помощью метода Exists,
а переместиться на нее — с помощью метода Goto объектов Document, Range
или Selection:
Rng := App.ActiveDocument.Goto(wdGoToBookmark, wdGoToNext, ,‘MyBookmark’); Rng.InsertAfter(‘Текст, вставленный после закладки’);
Значения констант для этого примера таковы:
const
wdGoToBookmark = $FFFFFFFF; //перейти к закладке wdGoToNext = $00000002; //искать следующий объект в тексте
Отметим, что с помощью метода Goto можно перемещаться не только на указанную
закладку, но и на другие объекты (рисунки, грамматические ошибки и др.), и направление
перемещения тоже может быть различным. Поэтому список констант, которые могут
быть использованы в качестве параметров данного метода, довольно велик.
|
Создание таблиц
Создавать таблицы можно двумя способами. Первый заключается в вызове метода
Add коллекции Tables объекта Document и последовательном заполнении ячеек данными.
Этот способ при позднем связывании работает довольно медленно.
Второй способ, намного более «быстрый», заключается в создании текста из нескольких
строк, содержащих подстроки с разделителями (в качестве разделителя можно использовать
любой или почти любой символ, нужно только, чтобы он заведомо не встречался
в данных, которые будут помещены в будущую таблицу), и последующей конвертации
такого текста в таблицу с помощью метода ConvertToTable объекта Range. Ниже
приведен пример создания таблицы из трех строк и трех столбцов этим способом
(в качеcтве разделителя, являющегося первым параметром метода ConvertToTable,
используется запятая):
Var Rng : Variant; ... Rng := App.Selection.Range; Rng.Collapse(wdCollapseEnd); Rng.InsertAfter(‘1, 2, 3’); Rng.InsertParagraphAfter; Rng.InsertAfter(‘4,5,6’); Rng.InsertParagraphAfter; Rng.InsertAfter(‘7,8,9’); Rng.InsertParagraphAfter; Rng.ConvertToTable(‘,’);
Отметим, что внешний вид таблицы можно изменить с помощью свойства Format,
а также с помощью свойств коллекции Columns, представляющей колонки таблицы,
и коллекции Rows, представляющей строки таблицы объекта Table.
|
Обращение к свойствам документа
Свойства документа можно получить с помощью коллекции BuiltInDocumentProperties
объекта Document, например:
Memo1.Lines.Add(‘Название — ‘ + _ App.ActiveDocument.BuiltInDocumentProperties [wdPropertyTitle].Value); Memo1.Lines.Add(‘Автор — ‘ + _ App.ActiveDocument.BuiltInDocumentProperties [wdPropertyAuthor]. _ Value); Memo1.Lines.Add(‘Шаблон — ‘ + _ App.ActiveDocument.BuiltInDocumentProperties [wdPropertyTemplate]. _Value)
Константы, необходимые для обращения к свойствам документа по имени, приведены
в листинге 3.
Итак, в данном разделе мы изучили основные операции, которые наиболее часто
применяются при автоматизации Microsoft Word. Естественно, возможности автоматизации
Word далеко не исчерпываются приведенными примерами, однако я надеюсь, что,
руководствуясь основными принципами создания контроллеров Word, изложенными
в данной статье, и соответствующим справочным файлом, вы сможете ими воспользоваться —
мы с вами уже убедились, что это вовсе не так сложно.
Следующим приложением Microsoft Office, автоматизацию которого мы рассмотрим,
будет Microsoft Excel — второе по популярности приложение Microsoft Office.
|
Автоматизация Microsoft Excel
В данном разделе мы обсудим наиболее часто встречающиеся задачи, связанные
с автоматизацией Microsoft Excel. Но перед этим рассмотрим, каковы программные
идентификаторы основных объектов Microsoft Excel и что представляет собой его
объектная модель.
|
Программные идентификаторы и объектная модель Microsoft Excel
Существует три типа объектов Excel, которые могут быть созданы непосредственно
с помощью приложения-контроллера. Эти объекты и соответствующие им программные
идентификаторы перечислены ниже.
Все остальные объекты Excel являются так называемыми внутренними объектами.
Небольшой фрагмент объектной модели Microsoft Excel изображен на рис.2:
Основным в объектной модели Excel является объект Application, содержащий коллекцию
Workbooks объектов типа WorkBook. Каждый объект типа WorkBook содержит коллекцию
WorkSheets-объектов типа WorkSheet, Charts типа Chart и др. Манипуляция рабочими
книгами, их листами, ячейками, диаграммами реально осуществляется путем обращения
к свойствам и методам этих объектов.
Ниже мы рассмотрим наиболее часто встречающиеся задачи, связанные с автоматизацией
Microsoft Excel. Если вам встретилась задача, не совпадающая ни с одной из перечисленных,
вы можете попытаться найти подходящий пример на Visual Basic в справочном файле
VBAXL9.CHM, либо, как и в случае Microsoft Word, записать соответствующий макрос
и проанализировать его код.
|
Запуск Microsoft Excel, создание и открытие рабочих книг
Для создания примеров использования Microsoft Excel можно использовать код
создания контроллера, приведенный в разделе «Общие принципы создания контроллеров
автоматизации», заменив первый оператор в приведенном примере на следующий:
AppProgID := ‘Excel.Application’;
и заменить комментарии кодом, манипулирующим свойствами и методами объекта
Excel.Application. Отметим, однако, что подключение контроллера автоматизации
к имеющейся версии Excel с помощью метода GetActiveOleObject может привести
к тому, что вся клиентская часть Excel окажется невидимой (это происходит, если
имеющаяся копия Excel запущена в режиме, когда ее пользовательский интерфейс
недоступен). Причины подобного поведения автору неизвестны. Поэтому, если существует
вероятность возникновения подобной ситуации, лучше упростить код создания контроллера
и всегда создавать новую копию Excel.
Изучение создания контроллеров Excel мы начнем с создания и открытия рабочих
книг.
Создать новую рабочую книгу Excel можно, используя метод Add коллекции Workbooks
объекта Application:
App.WorkBooks.Add;
Для создания рабочей книги на основе шаблона следует указать его имя в качестве
первого параметра метода Add:
App.WorkBooks.Add(‘C:Program FilesMicrosoft _ OfficeTemplates1033invoice.xlt’);
В качестве первого параметра этого метода можно также использовать следующие
константы:
const xlWBATChart = $FFFFEFF3; //рабочая книга состоит из листа с диаграммой xlWBATWorksheet = $FFFFEFB9; //рабочая книга состоит из листа с данными
В этом случае рабочая книга будет содержать один лист типа, заданного указанной
константой (график, обычный лист с данными и др.)
Для открытия уже существующего документа следует воспользоваться методом Open
коллекции WorkBooks:
App.Documents.Open(‘C:MyExcelFile.xls’);
Отметим, что свойство ActiveWorkBook объекта Excel.Application указывает на
текущую активную рабочую книгу среди одной или нескольких открытых. Помимо этого
к рабочей книге можно обращаться по ее порядковому номеру, например ко второй
открытой рабочей книге можно обратиться так:
App.WorkBooks[2]
Обратите внимание на то, что в Delphi при использовании позднего связывания
синтаксис, используемый для обращения к членам коллекций объектов Excel, отличен
от синтаксиса, используемого для обращения к объектам Word — в случае Word
мы использовали метод Item, а в случае Excel мы обращаемся к членам коллекции
как к элементам массива. Если же вы используете Visual Basic, синтаксис, применяемый
для обращения к членам коллекций, будет одинаков для всех коллекций Microsoft
Office.
Сделать рабочую книгу активной можно с помощью метода Activate:
App.WorkBooks[2].Activate;
Следующее, чему следует научиться — это сохранять рабочие книги в файлах.
|
Сохранение, печать и закрытие рабочих книг Microsoft Excel
Закрытие документа может быть осуществлено с помощью метода Close:
App.WorkBooks[2].Close;
или
App.ActiveWorkBook.Close;
Метод Close имеет несколько необязательных (в случае позднего связывания) параметров,
влияющих на правила сохранения рабочей книги. Первый из параметров принимает
значения True или False и влияет на то, сохранять ли изменения, внесенные в
рабочую книгу. Второй параметр (типа Variant) — имя файла, в котором нужно
сохранить рабочую книгу (если в нее были внесены изменения). Третий параметр,
также принимающий значения True или False, влияет на то, следует ли пересылать
документ следующему пользователю по электронной почте, и может быть проигнорирован,
если эта функциональность не используется.
App.ActiveWorkBook.Close(True,’C:MyWorkBook.xls’);
Просто сохранить рабочую книгу, не закрывая ее, можно с помощью методов Save
или SaveAs:
App.ActiveWorkBook.Save;
или
App.ActiveWorkBook.SaveAs(‘C:MyWorkBook.xls’);
Метод SaveAs имеет более десятка параметров, влияющих на то, как именно сохраняется
документ (под каким именем, с паролем или без него, какова кодовая страница
для содержащегося в ней текста и др.).
Закрыть сам Excel можно с помощью метода Quit объекта Excel.Application. В
случае Excel этот метод параметров не имеет.
Вывод документа Excel на устройство печати можно осуществить с помощью метода
PrintOut объекта WorkBook, например:
App.ActiveWorkBook.PrintOut;
Если нужно изменить параметры печати, следует указать значения соответствующих
параметров метода PrintOut (в случае Excel их восемь).
|
Обращение к листам и ячейкам
Обращение к листам рабочей книги производится с помощью коллекции WorkSheets
объекта WorkBook. Каждый член этой коллекции представляет собой объект WorkSheet.
К члену этой коллекции можно обратиться по его порядковому номеру, например:
App.WorkBooks[1].WorkSheets[1].Name := _ ‘Страница 1’;
Приведенный выше пример иллюстрирует, как можно изменить имя листа рабочей
книги.
К листу рабочей книги можно обратиться и по имени, например:
App.WorkBooks[1].WorkSheets[‘Sheet1’].Name := _ ‘Страница 1’;
Обращение к отдельным ячейкам листа производится с помощью коллекции Cells
объекта WorkSheet. Например, добавить данные в ячейку B1 можно следующим образом:
App.WorkBooks[1].WorkSheets[‘Sheet1’].Cells[1,2].Value:=’25’;
Здесь первая из координат ячейки указывает на номер строки, вторая — на
номер столбца.
Добавление формул в ячейки производится аналогичным способом:
App.WorkBooks[1].WorkSheets[‘Sheet1’] _ .Cells[3,2].Value := ‘ =SUM(B1:B2)’;
Очистить ячейку можно с помощью метода ClearContents.
Форматирование текста в ячейках производится с помощью свойств Font и Interior
объекта Cell и их подсвойств. Например, следующий фрагмент кода выводит текст
в ячейке красным жирным шрифтом Courier кегля 16 на желтом фоне:
App.WorkBooks[1].WorkSheets[1].Cells[3,2].Interior _ .Color := clYellow; App.WorkBooks[1].WorkSheets[1].Cells[3,2].Font _ .Color := clRed; App.WorkBooks[1].WorkSheets[1].Cells[3,2] _ .Font.Name := ‘Courier’; App.WorkBooks[1].WorkSheets[1].Cells[3,2] _ .Font.Size := 16; App.WorkBooks[1].WorkSheets[1].Cells[3,2] _ .Font.Bold := True;
Объект |
Программный идентификатор |
Комментарий |
---|---|---|
Application |
Excel.Application, Excel.Application.9 |
С помощью этого программного идентификатора создается экземпляр приложения |
WorkBook |
Excel.AddIn |
С помощью этого программного идентификатора создается экземпляр расширения |
Excel.Chart, Excel.Chart.8 |
Рабочая книга, созданная с помощью этих программных идентификаторов, |
|
Excel.Sheet, Excel.Sheet.8 |
Рабочая книга, созданная с помощью этих программных идентификаторов, |
Вместо свойства Color можно использовать свойство ColorIndex, принимающее значения
от 1 до 56; таблицу соответствий значений этого свойства реальным цветам можно
найти в справочном файле VBAXL9.CHM.
Обратиться к текущей ячейке можно с помощью свойства ActiveCell объекта Excel.Application,
а узнать местоположение ячейки можно с помощью свойства Address объекта Cell,
например:
ShowMessage(App.ActiveCell.Address);
Помимо обращения к отдельным ячейкам, можно манипулировать прямоугольными областями
ячеек с помощью объекта Range, например:
App.WorkBooks[1].WorkSheets[2].Range[‘A1:C5’] _ .Value := ‘Test’; App.WorkBooks[1].WorkSheets[2].Range[‘A1:C5’] _ .Font.Color := clRed;
Приведенный выше код приводит к заполнению прямоугольного участка текстом и
к изменению цвета шрифта ячеек.
Объект Range также часто используется для копирования прямоугольных областей
через буфер обмена. Ниже приведен пример, иллюстрирующий копирование такой области:
App.WorkBooks[1].WorkSheets[2].Range[‘A1:C5’].Copy; App.WorkBooks[1].WorkSheets[2].Range[‘A11:C15’].Select; App.WorkBooks[1].WorkSheets[2].Paste;
Обратите внимание на то, что диапазон, куда копируются данные, предварительно
выделяется с помощью метода Select.
Отметим, что примерно таким же образом можно копировать данные и из других
приложений (например, из Microsoft Word).
Довольно часто при автоматизации Excel используются его возможности, связанные
с построением диаграмм. Ниже мы рассмотрим, как это сделать.
|
Создание диаграмм
Диаграммам Excel соответствует объект Chart, который может располагаться как
на отдельном листе, так и на листе с данными. Если объект Chart располагается
на листе с данными, ему соответствует член коллекции ChartObjects объекта WorkSheet
и создание диаграммы нужно начать с добавления элемента в эту колекцию:
Ch:=App.WorkBooks[1].WorkSheets[2].ChartObjects.Add(10,50,400,400);
Параметрами этого метода являются координаты левого верхнего угла и размеры
диаграммы в пунктах (1/72 дюйма).
Если же диаграмма располагается на отдельном листе (не предназначенном для
хранения данных), то создание диаграммы нужно начать с добавления элемента в
коллекцию Sheets объекта Application (которая отличается от коллекции WorkSheets
тем, что содержит листы всех типов, а не только листы с данными):
App.WorkBooks[1].Sheets.Add(,,1,xlWBATChart);
В этом случае первый параметр метода Add указывает порядковый номер листа,
перед которым нужно поместить данный лист (или данные листы, если их несколько),
второй параметр — порядковый номер листа, после которого нужно поместить данный
лист (используется обычно один из них), третий параметр — сколько нужно
создать листов, а четвертый — какого типа должен быть лист. Значения четвертого
параметра совпадают со значениями первого параметра метода Add коллекции WorkBooks
объекта Application, и при использовании имен соответствующих констант следует
определить их в приложении-контроллере.
Простейший способ создать диаграмму, с точки зрения пользователя, — создать
ее с помощью соответствующего эксперта на основе прямоугольной области с данными.
Точно так же можно создать диаграмму и с помощью контроллера автоматизации —
для этой цели у объекта Chart, являющегося свойством объекта ChartObject (члена
коллекции ChartObjects), имеется метод ChartWizard. Первым параметром этого
метода является объект Range, содержащий диапазон ячеек для построения диаграммы,
а вторым — числовой параметр, указывающий, какого типа должна быть эта
диаграмма:
Var Ch: Variant; … Ch.Chart.ChartWizard _ (App.WorkBooks[1].WorkSheets[2].Range[‘A1:C5’], _ xl3DColumn);
Возможные значения параметра, отвечающего за тип диаграммы, можно найти в справочном
файле.
У объекта Chart имеется множество свойств, отвечающих за внешний вид диаграммы,
с помощью которых можно изменить ее точно так же, как пользователи делают это
вручную. Ниже приводится пример создания заголовка диаграммы и подписей вдоль
ее осей (отметим, что оси есть не у всех типов диаграмм).
Ch.Chart.HasTitle :=1; Ch.Chart.HasLegend := False; Ch.Chart.ChartTitle.Text := _ ‘Пример диаграммы Excel ‘; Ch.Chart.Axes(1).HasTitle := True; Ch.Chart.Axes(1).AxisTitle.Text := _ ‘Подпись вдоль оси абсцисс’; Ch.Chart.Axes(2).HasTitle := True; Ch.Chart.Axes(2).AxisTitle.Text := _ ‘Подпись вдоль оси ординат’;
Еще один способ создания диаграммы — определить все ее параметры с помощью
свойств объекта Chart, включая и определение серий, на основе которых она должна
быть построена. Данные для серии обычно находится в объекте Range, содержащем
строку или столбец данных, а добавление серии к диаграмме производится путем
добавления члена к коллекции SeriesCollection, например:
App.WorkBooks[1].Sheets.Add(,,1,xlWBATChart); App.WorkBooks[1].Sheets[1].ChartType := xl3DPie; Rng:=App.WorkBooks[1].WorkSheets[2].Range[‘B1:B5’]; App.WorkBooks[1].Sheets[1].SeriesCollection.Add(Rng);
В данном примере к диаграмме, созданной на отдельном листе, специально предназначенном
для диаграмм, добавляется одна серия на основе диапазона ячеек другого листа.
Мы изучили основные операции, применяемые при автоматизации Microsoft Excel.
Описания некоторых аналогичных операций для Microsoft Word, например обращение
к свойствам документов, были здесь опущены во избежание повторов. Возможности
автоматизации Microsoft Excel и Word далеко не исчерпываются приведенными примерами.
Сведения о них можно всегда найти в соответствующем справочном файле.
В следующих статьях данного цикла будет рассмотрена автоматизация других приложений
Microsoft Office — Microsoft PowerPoint, Microsoft Access, Microsoft Outlook
и др.
КомпьютерПресс 11’2000
- Download source code (Early binding) — 17.0 KB
- Download source code (Late binding) — 17.5 KB
Introduction
Word Automation is the development of software routines which help optimize or automate existing Word procedures. There are mainly two ways to automate Word using the technology used: Late binding and Early binding. Before coming to the automation, a brief introduction about these technologies is necessary.
Early Binding
Early binding is the commonly used technology. Here, we directly add a reference of the Office library to the project.
Then, we use the namespace in the code file and code using the classes and interfaces provided by the Office library. The code looks almost like:
public object CreateWordApplication() { Word.Application wordApp = new Word.ApplicationClass(); return wordApp; }
Early binding has several advantages:
- Code will run considerably faster.
- Debugging is very easy.
- You will get the environment Intellisense for coding (i.e., typing a classname or keyword and pressing dot will bring a popup). The main disadvantage is, it will support only the version that we have referenced in our application.
Late Binding
Late binding uses another mechanism called Reflection for invoking the Office libraries, and do not need any Office reference. It mainly uses two lines to communicate with the Office library:
Type wordType = Type.GetTypeFromProgID("Word.Application"); object wordApplication = Activator.CreateInstance(wordType);
By using this wordApplication
object, we can do almost all operations. The main advantages of late binding are, it will invoke our machine’s active Word application and is version independent. But its performance is slower than that of early binding.
By using early binding, developing an automation is very easy, and in this article, I am giving a brief idea about Word automation using late binding and early binding.
Problem Specification
Here, I am discussing a simple Word automation, which is used for getting word count from a document. We also replace a word with another word. First, we will implement it using early binding using a Word 2003 Office reference. After that, we will use late binding for solving the same problem in all Word versions.
Word Automation: Early Binding
The first step of doing a Word automation using early binding is installing a particular version of Office in your machine. Then, create a new Class Library named «WordAutomation» and give a reference of the Office Library in your project, as mentioned above.
Using Code
Using Namespaces
Create a new class named WordAutomation
in your project and use the following namespaces:
using Word; using Office;
Creating a WordApplication Object
The following code will create a Word Application object.
public object CreateWordApplication() { Word.Application wordApp = new Word.ApplicationClass(); return wordApp; }
Closing a WordApplication
Here, the parameter is an already created Word Application object using the CreateWordApplication()
function.
public bool CloseWordApp(object wordApplication) { bool isSuccess = false; if (wordApplication != null) { object missing = System.Reflection.Missing.Value; object saveChanges = Word.WdSaveOptions.wdSaveChanges; Word.Application wordApp = wordApplication as Word.ApplicationClass; wordApp.Quit(ref saveChanges, ref missing, ref missing); isSuccess = true; } return isSuccess; }
Creating a Word Document Object
The CreateWordDoc
function mainly requires a Word document file name and a Word application that we have created using the CreateWordApplication()
function. We can open it in either read only mode or read write mode.
public object CreateWordDoc(object fileName, object wordApplication, bool isReadonly) { Word.Document wordDoc = null; Word.Application wordApp = null; if (wordApplication != null) { wordApp = wordApplication as Word.ApplicationClass; } if (File.Exists(fileName.ToString()) && wordApp != null) { object readOnly = isReadonly; object isVisible = true; object missing = System.Reflection.Missing.Value; wordDoc = wordApp.Documents.Open(ref fileName, ref missing, ref readOnly, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref isVisible); } return wordDoc; }
Closing a Word Document Object
The CloseWordDoc()
function will close an already created Word document using the CreateWordDoc()
function.
public bool CloseWordDoc(object wordDocument, bool canSaveChange) { bool isSuccess = false; if (wordDocument != null) { object missing = System.Reflection.Missing.Value; object saveChanges = null; if (canSaveChange) { saveChanges = Word.WdSaveOptions.wdSaveChanges; } else { saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges; } Word.Document wordDoc = wordDocument as Word.Document; wordDoc.Close(ref saveChanges, ref missing, ref missing); isSuccess = true; } return isSuccess; }
Getting the Word Count
A Word document has different objects inside. The main area of a Word document is called Content. Other objects include Shapes, Comments, Header and Footer etc. This function will find out the word count from the above sections. The main parameters of this function is a Word document and a text to search in the document. Here, the main function to count the word occurrence is GetCountFromRange()
, which is explained below.
public int GetWordCount(object wordDoc, string word) { int count = 0; do { if (wordDoc == null) { break; } if (word.Trim().Length == 0) { break; } Word.Document wordDocument = wordDoc as Word.Document; wordDocument.Activate(); count+= GetCountFromRange(wordDocument.Content,wordDocument,word); foreach(Word.Comment com in wordDocument.Comments) { count+= GetCountFromRange(com.Range,wordDocument,word); break; } foreach(Word.HeaderFooter header in wordDocument.Sections.Last.Headers) { count+= GetCountFromRange(header.Range,wordDocument,word); } foreach(Word.HeaderFooter footer in wordDocument.Sections.Last.Footers) { count+= GetCountFromRange(footer.Range,wordDocument,word); } foreach (Word.Shape shape in wordDocument.Shapes) { if (shape.TextFrame.HasText < 0) { count+=GetCountFromRange( shape.TextFrame.TextRange,wordDocument,word); } } } while(false); return count; }
Getting Word Count from a Range
The word finds a text inside a range. The range may be the following.
WordDocument.Content
Comment.Range
HeaderFooter.Range
Shape.TextFrame.TextRange
etc.
We can search or replace a text by using a range, and the sample code is shown.
private int GetCountFromRange( Word.Range range, Word.Document wordDocument,string word) { int count = 0; object missing = System.Reflection.Missing.Value; object matchAllWord = true; object item = Word.WdGoToItem.wdGoToPage; object whichItem = Word.WdGoToDirection.wdGoToFirst; wordDocument.GoTo(ref item, ref whichItem,ref missing, ref missing); range.Find.ClearFormatting(); range.Find.Forward = true; range.Find.Text = word; range.Find.Execute( ref missing, ref missing, ref matchAllWord, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,ref missing, ref missing, ref missing, ref missing, ref missing); while (range.Find.Found) { ++count; range.Find.Execute( ref missing, ref missing, ref matchAllWord, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,ref missing, ref missing, ref missing, ref missing, ref missing); } return count; }
Find and Replace
The Find and Replace feature is similar to Find, the only difference is we need to provide the replace text as well. The function ReplaceRange()
will replace the Find text with the Replace text.
public bool FindReplace(object wordDoc,object wordApplication, string findText, string replaceText) { bool isSuccess = false; do { if (wordDoc == null) { break; } if (wordApplication == null) { break; } if (replaceText.Trim().Length == 0) { break; } if (findText.Trim().Length == 0) { break; } Word.Application wordApp = wordApplication as Word.ApplicationClass; Word.Document wordDocument = wordDoc as Word.Document; ReplaceRange(wordDocument.Content,wordDocument, wordApp,findText,replaceText); foreach (Word.Comment comment in wordDocument.Comments) { ReplaceRange(comment.Range, wordDocument, wordApp,findText,replaceText); } foreach (Word.HeaderFooter header in wordDocument.Sections.Last.Headers) { ReplaceRange(header.Range, wordDocument, wordApp,findText,replaceText); } foreach (Word.HeaderFooter footer in wordDocument.Sections.Last.Footers) { ReplaceRange(footer.Range, wordDocument,wordApp, findText,replaceText); } foreach (Word.Shape shp in wordDocument.Shapes) { if (shp.TextFrame.HasText < 0) { ReplaceRange(shp.TextFrame.TextRange, wordDocument,wordApp, findText,replaceText); } } isSuccess = true; } while(false); return isSuccess; }
Replace within a Range
This function will replace a Find text with a Replace text.
private void ReplaceRange(Word.Range range, Word.Document wordDocument, Word.Application wordApp, object findText, object replaceText) { object missing = System.Reflection.Missing.Value; wordDocument.Activate(); object item = Word.WdGoToItem.wdGoToPage; object whichItem = Word.WdGoToDirection.wdGoToFirst; object forward = true; wordDocument.GoTo(ref item, ref whichItem, ref missing, ref missing); object replaceAll = Word.WdReplace.wdReplaceAll; object matchAllWord = true; range.Find.ClearFormatting(); range.Find.Replacement.ClearFormatting(); range.Find.Execute(ref findText, ref missing, ref matchAllWord, ref missing, ref missing, ref missing, ref forward, ref missing, ref missing, ref replaceText, ref replaceAll, ref missing, ref missing, ref missing, ref missing); }
Word Automation: Late Binding
Late binding does not require any references and namespace usage in the project. Coding is very difficult, and we have to know the functions and properties.
Using Code
Using Namespaces
For working with late binding, you have to use the Reflection mechanism. So the following namespace usage is required:
using System.Reflection;
Creating a WordApplication Object
If at least one Word version is installed in your machine, this function creates a Word type using the program ID of Word. If the system has multiple versions, then at a time, only one of the Word versions is active and this function will create the type of the active version. The CreateInstance
method will create the Word Application object.
public object CreateWordApplication() { string message = "Failed to create word application. Check whether" + " word installation is correct."; Type wordType = Type.GetTypeFromProgID("Word.Application"); if (wordType == null) { throw new Exception(message); } object wordApplication = Activator.CreateInstance(wordType); if (wordApplication == null) { throw new Exception(message); } return wordApplication; }
Closing a WordApplication
For closing a Word application, we have to invoke the Quit
method of the Word application. Here, another method is used to invoke the Quit
function, called InvokeMethod()
, and is explained in the section below. The parameter wordApplication
is the instance created from the CreateWordApplication()
function.
public bool CloseWordApp(object wordApplication) { bool isSuccess = false; if (wordApplication != null) { object saveChanges = -1; InvokeMember("Quit",wordApplication,new object[]{saveChanges}); isSuccess = true; } return isSuccess; }
Creating a Word Document Object
Here, wordApplication
and file name are required to create a Word document. Inside the function, we use the GetProperty()
function for getting the property named Documents
from the Word application, and it is explained in the section below.
public object CreateWordDoc(object fileName, object wordApplication, bool isReadonly) { object wordDocument = null; if (File.Exists(fileName.ToString()) && wordApplication != null) { object readOnly = isReadonly; object isVisible = true; object missing = System.Reflection.Missing.Value; object wordDocuments = GetProperty("Documents", wordApplication); wordDocument = InvokeMember("Open",wordDocuments, new object[]{fileName, missing, isReadonly, missing, missing, missing, missing, missing, missing, missing, missing, isVisible}); } return wordDocument; }
Closing a Word Document Object
This function requires mainly two parameters: a Word document and save changes information. The main thing to understand here is, we have to pass a save changes information to the Close()
function of the Word document, which is an enum value (Word.WdSaveOptions
), and you can refer the same function in early binding for better understanding. But instead of that enum member, we will just pass its integer value here, because we don’t have any access to that enum here. Here, the value 1 is the integer value of Word.WdSaveOptions.wdSaveChanges
, and the value 0 is the integer value of Word.WdSaveOptions.wdDoNotSaveChanges
.
public bool CloseWordDoc(object wordDocument, bool canSaveChange) { bool isSuccess = false; if (wordDocument != null) { object saveChanges = null; if (canSaveChange) { saveChanges = -1; } else { saveChanges = 0; } InvokeMember("Close",wordDocument, new object[]{saveChanges}); isSuccess = true; } return isSuccess; }
Getting the Word Count
Compare the below function with the late binding technology and try to understand how the early binding code is converted to late binding.
public int GetWordCount(object wordDoc, string word) { int count = 0; do { if (wordDoc == null) { break; } if (word.Trim().Length == 0) { break; } InvokeMember("Activate",wordDoc); object content = GetProperty("Content",wordDoc); count+= GetCountFromRange(content,wordDoc,word); object comments = GetProperty("Comments",wordDoc); object count1 = GetProperty("Count", comments); int rangeCount = (int)count1; for(int i = 1; i <= rangeCount;) { object comment = InvokeMember("Item", comments, new object[] { i }); object range = GetProperty("Range", comment); count+= GetCountFromRange(range,wordDoc,word); break; } object sections = GetProperty("Sections",wordDoc); object last = GetProperty("Last",sections); object headers = GetProperty("Headers",last); rangeCount = (int)GetProperty("Count", headers); for (int i = 1; i <= rangeCount; i++) { object header = InvokeMember("Item", headers, new object[] { i }); object range = GetProperty("Range", header); count+= GetCountFromRange(range,wordDoc,word); } object footers = GetProperty("Footers",last); rangeCount = (int)GetProperty("Count", footers); for (int i = 1; i <= rangeCount; i++) { object footer = InvokeMember("Item", footers, new object[] { i }); object range = GetProperty("Range", footer); count+= GetCountFromRange(range,wordDoc,word); } object shapes = GetProperty("Shapes",wordDoc); rangeCount = (int)GetProperty("Count", shapes); for (int i = 1; i <= rangeCount; i++) { object shape = InvokeMember("Item", shapes, new object[] { i }); object textFrame = GetProperty("TextFrame",shape); int hasText = (int)GetProperty("HasText",textFrame); if (hasText < 0) { object range = GetProperty("TextRange", textFrame); count+= GetCountFromRange(range,wordDoc,word); } } } while(false); return count; }
Getting Word Count from a Range
Here also, the logic is the same as early binding, only a conversion is done.
private int GetCountFromRange(object range, object wordDocument,string word) { int count = 0; object missing = System.Reflection.Missing.Value; object matchAllWord = true; object item = 1; object whichItem = 1; InvokeMember("GoTo",wordDocument,new object[]{item, whichItem}); object find = GetProperty("Find",range); InvokeMember("ClearFormatting",find); SetProperty("Forward",find,true); SetProperty("Text",find,word); SetProperty("MatchWholeWord",find,true); InvokeMember("Execute",find); bool found = (bool)GetProperty("Found",find); while (found) { ++count; InvokeMember("Execute",find); found = (bool)GetProperty("Found",find); } return count; }
Find and Replace
The Find and Replace is similar to Find, the only difference is we need to provide the replace text as well. The function ReplaceRange()
will replace the find text with the replace text.
public bool FindReplace(object wordDoc,object wordApplication, string findText, string replaceText) { bool isSuccess = false; do { if (wordDoc == null) { break; } if (wordApplication == null) { break; } if (replaceText.Trim().Length == 0) { break; } if (findText.Trim().Length == 0) { break; } object content = GetProperty("Content",wordDoc); ReplaceRange(content,wordDoc,wordApplication,findText,replaceText); object comments = GetProperty("Comments",wordDoc); object count1 = GetProperty("Count", comments); int rangeCount = (int)count1; for(int i = 1; i <= rangeCount; i++) { object comment = InvokeMember("Item", comments, new object[] { i }); object range = GetProperty("Range", comment); ReplaceRange(range,wordDoc,wordApplication,findText,replaceText); } object sections = GetProperty("Sections",wordDoc); object last = GetProperty("Last",sections); object headers = GetProperty("Headers",last); rangeCount = (int)GetProperty("Count", headers); for (int i = 1; i <= rangeCount; i++) { object header = InvokeMember("Item", headers, new object[] { i }); object range = GetProperty("Range", header); ReplaceRange(range,wordDoc,wordApplication,findText,replaceText); } object footers = GetProperty("Footers",last); rangeCount = (int)GetProperty("Count", footers); for (int i = 1; i <= rangeCount; i++) { object footer = InvokeMember("Item", footers, new object[] { i }); object range = GetProperty("Range", footer); ReplaceRange(range,wordDoc,wordApplication,findText,replaceText); } object shapes = GetProperty("Shapes",wordDoc); rangeCount = (int)GetProperty("Count", shapes); for (int i = 1; i <= rangeCount; i++) { object shape = InvokeMember("Item", shapes, new object[] { i }); object textFrame = GetProperty("TextFrame",shape); int hasText = (int)GetProperty("HasText",textFrame); if (hasText < 0) { object range = GetProperty("TextRange", textFrame); ReplaceRange(range,wordDoc,wordApplication,findText,replaceText); } } isSuccess = true; } while(false); return isSuccess; }
Replace Within a Range
This function will replace a find text with a replace text within a range.
private void ReplaceRange(object range, object wordDocument,object wordApp, string findText, string replaceText) { object missing = System.Reflection.Missing.Value; InvokeMember("Activate",wordDocument); object item = 1; object whichItem = 1; InvokeMember("GoTo",wordDocument,new object[]{item, whichItem}); object replaceAll = 2; object matchAllWord = true; object find = GetProperty("Find",range); InvokeMember("ClearFormatting",find); object replacement = GetProperty("Replacement",find); InvokeMember("ClearFormatting",replacement); InvokeMember("Execute",find, new object[]{findText,false,true,missing,missing, missing,true,missing,missing, replaceText,replaceAll}); }
Reflection Utilities
Invoking a Method
private object InvokeMember(string method, object instance, object[] parameters) { Type type = instance.GetType(); return type.InvokeMember(method, BindingFlags.InvokeMethod,null,instance,parameters); }
Invoking a Method
private object InvokeMember(string method, object instance) { Type type = instance.GetType(); return type.InvokeMember(method, BindingFlags.InvokeMethod,null,instance,new object[]{}); }
Getting a Property
private object GetProperty(string propertyName, object instance) { Type type = instance.GetType(); return type.InvokeMember(propertyName, BindingFlags.GetProperty,null,instance,new object[]{}); }
Setting a Property
private void SetProperty(string propertyName,object instance, object value) { Type type = instance.GetType(); type.InvokeMember(propertyName,BindingFlags.SetProperty, null,instance,new object[]{ value} ); }
Points of Interest
Getting Office Class Details
Before going to late binding type of coding, you have to know the object details of Office libraries. For this purpose, one of the better solutions is the Object Browser of Office applications. For getting the Object Browser, open Microsoft Word, open the menu Tools->Macro->Visual Basic Editor, or press Alt+F11. This will open a new Microsoft Visual Basic Environment. From this editor, open the View menu and click on the Object Browser submenu, or press F2. You will get the following window and all the object information is listed there.
— C#, WPF, WCF
— Java, J2EE, jersey, Spring MVC, Spring Boot, Spring Security, Hibernate, Spring Data
— NoSQL with Cassandra
— C, C++, Qt, OpenGL, Python
— SQL Server, Oracle, PostgreSQL
— Glassfish, Apache, Tomcat, NodeJS and IIS
— Hadoop, Flume, Spark
— Amazon Web Services (AWS) & Jenkins
— Big Data and Analytics
Время на прочтение
3 мин
Количество просмотров 15K
Значительная часть текстовых документов сегодня создаётся и редактируется в программе Microsoft Word. Наличие этой программы практически на каждом компьютере даёт возможность автоматизировать печать, обработку и экспорт текстовых документов в PDF, используя API Microsoft Word.
В этой статье я расскажу, как автоматизировать печать текстовых документов и конвертирование документов в формат PDF.
Пример исходного кода для печати текстового документа, написанный на языке Microsoft Visual Basic.NET, выглядит так:
Dim app = CreateObject("Word.Application")
Dim doc = app.Documents.Open("D:inmy resume.doc")
app.PrintOut(False)
doc.Close()
app.Quit()
Этот код выглядит очень просто. Но для автоматизации печати списка документов следует учесть ряд нюансов:
1. Людям нужна возможность задавать имя используемого принтера
2. Нужно заставить программу Word работать в невидимом режиме, а не появляться на экране при обработке каждого документа
3. Нужно отключить все информационные сообщения и запросы на подтверждение операций с документами
4. Нужно отключить добавление обработанных в автоматическом режиме файлов в список «Recent files».
Доработанный вариант нашей программы будет выглядеть так:
Dim app = CreateObject("Word.Application")
app.Visible = False
app.DisplayAlerts = 0
app.FeatureInstall = 0
app.DisplayRecentFiles = False
app.DisplayDocumentInformationPanel = False
app.AutomationSecurity = 3
Dim wdOptions = app.Options
wdOptions.WarnBeforeSavingPrintingSendingMarkup = False
wdOptions.SavePropertiesPrompt = False
wdOptions.DoNotPromptForConvert = True
wdOptions.PromptUpdateStyle = False
wdOptions.ConfirmConversions = False
Dim doc = app.Documents.Open("D:inmy resume.doc")
doc.Application.ActivePrinter = "Xerox Global Print Driver PS"
app.PrintOut(False)
doc.Saved = True
doc.Close(0)
app.Quit()
Я считаю, что этого кода вполне достаточно, что бы сдать зачёт по программированию в университете. Но до внедрения его в коммерческое приложение следует решить еще несколько вопросов.
1. Этот код не будет работать, если входной файл имеет атрибут «Read only».
2. Производительность вашей программы можно увеличить в несколько раз, если открывать и закрывать только документы, а приложение Microsoft Word оставлять открытым.
3. Многим пользователям нужна возможность печати только заданного диапазона страниц, а не всего документа целиком, а так же возможность напечатать несколько копий документа.
4. Этот код не будет работать, если ваша программа запущена из Windows Tasks Scheduler или от имени Windows Service.
В этой статье я не стану рассказывать о том, как решить эти четыре задачи, что бы не делать жизнь моих читателей слишком скучной. Скажу лишь, что в программах Print Conductor, FolderMill и 2Printer они были мной успешно решены.
Для конвертирования документа в PDF нужно заменить функцию PrintOut на ExportAsFixedFormat. После этого наш код будет выглядеть так:
Dim app = CreateObject("Word.Application")
app.Visible = False
app.DisplayAlerts = 0
app.FeatureInstall = 0
app.DisplayRecentFiles = False
app.DisplayDocumentInformationPanel = False
app.AutomationSecurity = 3
Dim wdOptions = app.Options
wdOptions.WarnBeforeSavingPrintingSendingMarkup = False
wdOptions.SavePropertiesPrompt = False
wdOptions.DoNotPromptForConvert = True
wdOptions.PromptUpdateStyle = False
wdOptions.ConfirmConversions = False
Dim doc = app.Documents.Open("D:inmy resume.doc")
doc.ExportAsFixedFormat("D:outmy resume.pdf", 17)
doc.Saved = True
doc.Close(0)
app.Quit()
Этот пример будет работать, если на вашем компьютере Microsoft Word 2007 SP2 или его более новая версия.
Функция ExportAsFixedFormat не будет работать, если на компьютере не установлено ни одного принтера. Либо если один из установленных принтеров не назначен в качестве system default printer.
Код экспорта документов в PDF можно улучшить, реализовав возможность экспорта части страниц документа, а так же возможность экспорта документов в PDF/A. Обе эти возможности компания Microsoft реализовала в функции ExportAsFixedFormat.
Примеры коммерческих приложений, использующих этот код для конвертирования документов в PDF: DocuFreezer и FolderMill.
На прошлой неделе постоянный читатель сайта задал мне вопрос об автоматизации своей работы с редактором Word. Потребовалось некоторое время, чтобы найти ответ. И в результате появилась очередная статья в онлайн-журнале «Компьютерра».
Теперь публикую и на сайте.
Вопрос:
Я знаю, что в Word есть функция, когда какая-либо комбинация клавиш меняется на текст, который я заранее ввел. Это удобно, если одна и та же фраза или слово много раз повторяется в документе. Мне казалось, что это в «Автозамене», но у меня ничего не получилось. Подскажите, пожалуйста правильный ответ.
Действительно, такая возможность есть. Правда, для того чтобы назначить какому-либо часто повторяющемуся словосочетанию «горячую клавишу» придется немного пощелкать мышкой.
Во-первых, определитесь со словосочетанием, которое вы постоянно используете в своих текстах. Возможно, оно у вас не одно.
Во-вторых, занесите это словосочетание в Автотекст
Автотекст. Текстовые или графические элементы, которые можно многократно использовать в документах, например стандартные пункты договора или список отпускаемых товаров. Каждый выделенный фрагмент текста или графический объект сохраняется в виде элемента автотекста и получает свое уникальное имя. (из Справки Word)
Для этого создайте пустой документ и напечатайте ваше словосочетание. Затем выделите его и нажмите сочетание клавиш «Alt+F3». Появится подтверждающее окно Создание элемента автотекста (Create AutoText) с вашим словосочетанием, которое вы можете отредактировать или ввести иное словосочетание.
Нажмите кнопку OK для подтверждения. Все, ваше словосочетание занесено в Автотекст.
Создать новый элемент автотекста можно и другим, более трудоемким способом — через меню Вставка (Insert), в котором вам следует выбрать из подменю команды Автотекст (AutoText) такую же одноименную команду. Откроется окно Автозамена (AutoCorrect) как раз на вкладке Автотекст.
В поле шмя элемента (Enter AutoText entries here) введите вашу часто повторяющуюся фразу и нажмите кнопку Добавить (Add).
Итак, фраза для автоматической вставки в ваш документ создана, теперь необходимо присвоить ей какое-нибудь сочетание клавиш, используя которое вы легко и быстро добавите вашу фразу в документ.
Для этого:
- Выберите из меню Сервис (Tools) команду Настройка (Customize). Откроется диалоговое окно, внизу которого вы увидите кнопку Клавиатура (Keyboard). Нажмите ее и перед вами появится новое окно Настройка клавиатуры (Customize Keyboard), как на скриншоте ниже:
- В левой области окна выберите ((Выбрать — означает щелкнуть мышкой на нужном элементе, который сразу станет подсвеченным)) категорию Автотекст (AutoText) — она находится в самом низу категорий — а справа в поле Автотекст (AutoText) выберите вашу фразу/словосочетание.
- Чуть ниже, в поле Новое сочетание клавиш (Press new shortcut key), введите ваше сочетание клавиш, используя функциональные клавиши: Ctrl, Shift или Alt. Причем клавиша Shift может присутствовать только в сочетании с одной из двух своих подруг: Ctrl или Alt.
Обратите внимание: если выбранное вами сочетание клавиш («горячие клавиши») уже используются иным элементом автотекста, то ниже вы увидите неприметное поле Текущее назначение и тот элемент автотекста, за которым зарезервированы эти горячие клавиши. Просто выберите другое сочетание клавиш. Для этого достаточно очистить поле, нажав на клавитуре клавишу Delete и ввести новый вариант.
- Теперь осталось нажать кнопку Назначить (Assign) и закрыть все окна.
Таким образом, выполнив эти шаги, вы существенно увеличите эффективность своей работы в текстовом редакторе Word, и, быть может, поднимите свой авторитет среди коллег. Ведь они-то так не могут!