Word, Excel, PowerPoint, Outlook, and Access all use the same VBA language; the only difference between them is their object models (for example, Excel has a Workbooks
object, Word has Documents
). Any one of these applications can access another application’s object model as long as the second application is installed.
To access Word’s object library, Excel must establish a link to it. There are two ways of doing this: early binding or late binding. With early binding, the reference to the application object is created when the program is compiled; with late binding, it is created when the program is run.
This chapter is an introduction to accessing Word from Excel; we will not be reviewing Word’s entire object model or the object models of other applications. Refer to the VBA Object Browser in the appropriate application to learn about other object models.
Code written with early binding executes faster than code with late binding. A reference is made to Word’s object library before the code is written so that Word’s objects, properties, and methods are available in the Object Browser. Tips also appear, as shown in Figure 18.1, such as a list of members of an object.
The disadvantage of early binding is that the referenced object library must exist on the system. For example, if you write a macro referencing Word 2007’s object library and someone with Word 2003 attempts to run the code, the program fails because the program cannot find the 2007 object library.
The object library is added through the VB Editor:
Select Tools, References.
Check Microsoft Word 12.0 Object Library in the Available References list (see Figure 18.2).
Click OK.
If the object library is not found, Word is not installed. If another version is found in the list (such as 10.0), another version of Word is installed.
After the reference is set, Word variables can be declared with the correct (Word) variable type. However, if the object variable is declared As Object
, this forces the program to use late binding:
Sub WordEarlyBinding() Dim wdApp As Word.Application Dim wdDoc As Document Set wdApp = New Word.Application Set wdDoc = wdApp.Documents.Open(ThisWorkbook.Path & _ "Chapter 18 - Automating Word.docx") wdApp.Visible = True Set wdApp = Nothing Set wdDoc = Nothing End Sub
Excel searches through the selected libraries to find the reference for the object type. If the type is found in more than one library, the first reference is selected. You can influence which library is chosen by changing the priority of the reference in the listing.
This example creates a new instance of Word and opens an existing Word document from Excel. The declared variables, wdApp
and wdDoc
, are of Word object types. wdApp
is used to create a reference to the Word application in the same way the Application
object is used in Excel. New Word.Application
is used to create a new instance of Word.
If you are opening a document in a new instance of Word, Word is not visible. If the application needs to be shown, it must be unhidden (wdApp.Visible = True
).
When finished, it’s a good idea to set the object variables to Nothing
and release the memory being used by the application, as shown here:
Set wdApp = Nothing Set wdDoc = Nothing
If the referenced version of Word does not exist on the system, an error message appears, as shown in Figure 18.3. View the References list; the missing object is highlighted with the word MISSING (see Figure 18.4).
If a previous version of Word is available, you can try running the program with that version referenced. Many objects are the same between versions.
When using late binding, you are creating an object that refers to the Word application before linking to the Word library. Because you do not set up a reference beforehand, the only constraint on the Word version is that the objects, properties, and methods must exist. In the case where there are differences, the version can be verified and the correct object used accordingly.
The disadvantage of late binding is that Excel doesn’t know what is going on—it doesn’t understand that you are referring to Word. This prevents the tips from appearing when referencing Word objects. Also, built-in constants are not available and, when compiling, Excel cannot verify that the references to Word are correct. After the program is executed, the links to Word begin to build, and any coding errors are detected at that point.
The following example creates a new instance of Word and then opens and makes visible an existing Word document:
Sub WordLateBinding() Dim wdApp As Object, wdDoc As Object Set wdApp = CreateObject("Word.Application") Set wdDoc = wdApp.Documents.Open(ThisWorkbook.Path & _ "Chapter 18 - Automating Word.docx") wdApp.Visible = True Set wdApp = Nothing Set wdDoc = Nothing End Sub
An object variable (wdApp
) is declared and set to reference the application (CreateObject("Word.Application")
). Other required variables are then declared (wdDoc
), and the application object is used to refer these variables to Word’s object model.
The following sections describe how to create new objects as well as how to reference currently open objects.
In the early binding example, the keyword New
was used to reference the Word application. The New
keyword can be used only with early binding; it does not work with late binding. CreateObject
or GetObject
would also work, but New
was best for that example. If an instance of the application is running and you want to use it, use the GetObject
function instead.
If your code to open Word runs smoothly, but you don’t see an instance of Word (and should), open your Task Manager and look for the process WinWord.exe. If it exists, from the Immediate window in Excel’s VB Editor, type the following (early binding):
Word.Application.Visible = True
If multiple instances of WinWord.exe are found, you must make each visible and close the extra instance(s) of WinWord.exe.
The CreateObject
function was used in the late binding example, but can also be used in early binding. CreateObject
has a class
parameter consisting of the name and type of the object to be created (Name.Type
). For example, in the examples I’ve shown you (Word.Application
), Word is the Name
, and Application is the Type
.
It creates a new instance of the object; in this case, the Word application is created.
The GetObject
function can be used to reference an instance of Word that is already running. It creates an error if no instance can be found.
GetObject
’s two parameters are optional. The first parameter specifies the full path and filename to open, and the second parameter specifies the application program. In the following example, we leave off the application, allowing the default program (which is Word) to open the document:
Sub UseGetObject() Dim wdDoc As Object Set wdDoc = GetObject(ThisWorkbook.Path & "Chapter 18 - Automating Word.docx") wdDoc.Application.Visible = True Set wdDoc = Nothing End Sub
This example opens a document in an existing instance of Word and ensures the Word application’s Visible
property is set to True
. Note that to make the document visible, you have to refer to the application object (wdDoc.Application.Visible
) because wdDoc
is referencing a document rather than the application.
Although the Word application’s Visible
property is set to True
, this code does not make the Word application the active application. In most cases, the Word application icon stays in the taskbar, and Excel remains the active application on the user’s screen.
The following example uses errors to learn whether Word is already open before pasting a chart at the end of a document. If not, it opens Word and creates a new document:
Sub IsWordOpen() Dim wdApp As Word.Application ActiveChart.ChartArea.Copy On Error Resume Next Set wdApp = GetObject(, "Word.Application") If wdApp Is Nothing Then Set wdApp = GetObject("", "Word.Application") With wdApp .Documents.Add .Visible = True End With End If On Error GoTo 0 With wdApp.Selection .EndKey Unit:=wdStory .TypeParagraph .PasteSpecial Link:=False, DataType:=wdPasteOLEObject, _ Placement:=wdInLine, DisplayAsIcon:=False End With Set wdApp = Nothing End Sub
Using On Error Resume Next
forces the program to continue even if it runs into an error. In this case, an error occurs when we attempt to link wdApp
to an object that does not exist. wdApp
will have no value. The next line, If wdApp Is Nothing then
, takes advantage of this and opens an instance of Word, adding an empty document and making the application visible. Note the use of empty quotes for the first parameter in GetObject("", "Word.Application")
—this is how to use the GetObject
function to open a new instance of Word. Use On Error Goto 0
to return to normal VBA handling behavior.
In the previous example, we used constants that are specific to Word, such as wdPasteOLEObject
and wdInLine
. When you’re programming using early binding, Excel helps you out by showing these constants in the tip window.
With late binding, these tips won’t appear. So what can you do? You might write your program using early binding, then, after you’ve compiled and tested it, change it to late binding. The problem with this method is that it won’t compile because Excel doesn’t recognize the Word constants.
The words wdPasteOLEObject and wdInLine are for your convenience as a programmer. Behind each of these text constants is the real value that VBA understands. The solution to this is to retrieve and use these real values with your late binding program.
One way to retrieve the value is to add a watch for the constants. Then, step through your code and check the value of the constant as it appears in the Watch window, as shown in Figure 18.5.
A second method is to look up the constant in the Object Browser. You’ll need the Word library set up as a reference. Right-click in the constant, select Definition, and the Object Browser opens to the constant, showing you the value in the bottom window, as shown in Figure 18.6.
You can set up the Word reference library to access it from the Object Browser, but you don’t have to set up your code with early binding. In this way, you can have the reference at your fingertips, but your code is still late binding. Turning off the reference library is just a few clicks away.
Replacing the constants in the earlier code example with their real values would look like this:
With wdApp.Selection .EndKey Unit:=6 .TypeParagraph .PasteSpecial Link:=False, DataType:=0, _ Placement:=0, DisplayAsIcon:=False End With
But what happens a month from now when you return to the code and you’re trying to remember what those numbers mean? The solution is up to you. Some programmers just add comments to the code referencing the Word constant. Other programmers create their own variables to hold the real value and use those variables in place of the constants, like this:
Const xwdStory As Long = 6 Const xwdPasteOLEObject As Long = 0 Const xwdInLine As Long = 0 With wdApp.Selection .EndKey Unit:=xwdStory .TypeParagraph .PasteSpecial Link:=False, DataType:=xwdPasteOLEObject, _ Placement:=xwdInLine, DisplayAsIcon:=False End With
Word’s macro recorder can be used to get a preliminary understanding of the Word object model. However, much like Excel’s macro recorder, the results will be long-winded. Keep this in mind and use the recorder to lead you toward the objects, properties, and methods in Word.
The macro recorder is limited in what it will allow you to record. The mouse cannot be used to move the cursor or select objects, but there are no limits on doing so with the keyboard.
The following example is what the Word macro recorder produces when adding a new, blank document.
Documents.Add Template:="Normal", NewTemplate:=False, DocumentType:=0
Making this more efficient (still in Word) produces this:
Documents.Add
Template
, NewTemplate
, and DocumentType
are all optional properties that the recorder includes but are not required unless you need to change a default property or ensure that a property is as you require.
To use the same line of code in Excel, a link to the Word object library is required, as you learned earlier. After that link is established, an understanding of Word’s objects is all you need. Following is a review of some of Word’s objects—enough to get you off the ground. For a more detailed listing, refer to the object model in Word’s VB Editor.
Word’s Document
object is equivalent to Excel’s Workbook
object. It consists of characters, words, sentences, paragraphs, sections, and headers/footers. It is through the Document
object that methods and properties affecting the entire document, such as printing, closing, searching, and reviewing, are accomplished.
To create a blank document in an existing instance of Word, use the Add
method (we already learned how to create a new document when Word is closed—refer to GetObject
and CreateObject
):
Sub NewDocument() Dim wdApp As Word.Application Set wdApp = GetObject(, "Word.Application") wdApp.Documents.Add Set wdApp = Nothing End Sub
This example opens a new, blank document that uses the default template. To create a new document that uses a specific template, use this:
wdApp.Documents.Add Template:="Contemporary Memo.dot"
This creates a new document that uses the Contemporary Memo template. Template
can be either just the name of a template from the default template location or the file path and name.
To open an existing document, use the Open
method. Several parameters are available, including Read Only
and AddtoRecentFiles
. The following example opens an existing document as Read Only
, but prevents the file from being added to the Recent File List under the File menu:
wdApp.Documents.Open _ Filename:="C:Excel VBA 2007 by Jelen & SyrstadChapter 19 - Arrays.docx", _ ReadOnly:=True, AddtoRecentFiles:=False
After changes have been made to a document, you most likely will want to save it. To save a document with its existing name, use this:
wdApp.Documents.Save
If the Save
command is used with a new document without a name, the Save As dialog box appears. To save a document with a new name, you can use the SaveAs
method instead:
wdApp.ActiveDocument.SaveAs "C:Excel VBA 2007 by Jelen & SyrstadMemoTest.docx"
SaveAs
requires the use of members of the Document
object, such as ActiveDocument
.
Use the Close
method to close a specified document or all open documents. By default, a Save dialog appears for any documents with unsaved changes. The SaveChanges
argument can be used to change this. To close all open documents without saving changes, use this code:
wdApp.Documents.Close SaveChanges:=wdDoNotSaveChanges
To close a specific document, you can close the active document or you can specify a document name:
wdApp.ActiveDocument.Close
or
wdApp.Documents("Chapter 19 - Arrays.docx").Close
Use the PrintOut
method to print part or all of a document. To print a document with all the default print settings, use this:
wdApp.ActiveDocument.PrintOut
By default, the print range is the entire document, but this can be changed by setting the Range
and Pages
arguments of the PrintOut
method:
wdApp.ActiveDocument.PrintOut Range:=wdPrintRangeOfPages, Pages:="2"
The Selection
object represents what is selected in the document—that is, a word, sentence, or the insertion point. It has a Type
property that returns the type of what is selected (wdSelectionIP
, wdSelectionColumn
, wdSelectionShape
, and so on).
The HomeKey
and EndKey
methods are used to change the selection; they correspond to using the Home and End keys, respectively, on the keyboard. They have two parameters: Unit
and Extend
. Unit
is the range of movement to make, either to the beginning (Home
) or end (End
) of a line (wdLine
), document (wdStory
), column (wdColumn
), or row (wdRow
). Extend
is the type of movement: wdMove
moves the selection, wdExtend
extends the selection from the original insertion point to the new insertion point.
To move the cursor to the beginning of the document, use this code:
wdApp.Selection.HomeKey Unit:=wdStory, Extend:=wdMove
To select the document from the insertion point to the end of the document, use this code:
wdApp.Selection.EndKey Unit:=wdStory, Extend:=wdExtend
The TypeText
method is used to insert text into a Word document. User settings, such as the Overtype
setting, can affect what will happen when text is inserted into the document:
Sub InsertText() Dim wdApp As Word.Application Dim wdDoc As Document Dim wdSln As Selection Set wdApp = GetObject(, "Word.Application") Set wdDoc = wdApp.ActiveDocument Set wdSln = wdApp.Selection wdDoc.Application.Options.Overtype = False With wdSln If .Type = wdSelectionIP Then .TypeText ("Inserting at insertion point. ") ElseIf .Type = wdSelectionNormal Then If wdApp.Options.ReplaceSelection Then .Collapse Direction:=wdCollapseStart End If .TypeText ("Inserting before a text block. ") End If End With Set wdApp = Nothing Set wdDoc = Nothing End Sub
The Range
object uses the following syntax:
Range(StartPosition, EndPosition)
The Range
object represents a contiguous area, or areas, in the document. It has a starting character position and an ending character position. The object can be the insertion point, a range of text, or the entire document, including nonprinting characters (such as spaces or paragraph marks).
The Range
object is similar to the Selection
object but, in some ways, is better: It requires less code to accomplish the same tasks; it has more capabilities; and it saves time and memory because the Range
object doesn’t require Word to move the cursor or highlight objects in the document to manipulate them.
To define a range, enter a starting and ending position, as shown in this code segment:
Sub RangeText() Dim wdApp As Word.Application Dim wdDoc As Document Dim wdRng As Word.Range Set wdApp = GetObject(, "Word.Application") Set wdDoc = wdApp.ActiveDocument Set wdRng = wdDoc.Range(0, 22) wdRng.Select Set wdApp = Nothing Set wdDoc = Nothing Set wdRng = Nothing End Sub
Figure 18.7 shows the results of running this code. The first 22 characters, including nonprinting characters such as paragraph returns, are selected.
The range was selected (wdRng.Select
) for easier viewing. It is not required that the range be selected to be manipulated. For example, to delete the range, do this:
wdRng.Delete
The first character position in a document is always zero, and the last is equivalent to the number of characters in the document.
The Range
object also selects paragraphs. The following example copies the third paragraph in the active document and pastes it in Excel. Depending on how the paste is done, the text can be pasted into a text box (see Figure 18.8) or into a cell (see Figure 18.9):
Sub SelectSentence() Dim wdApp As Word.Application Dim wdRng As Word.Range Set wdApp = GetObject(, "Word.Application") With wdApp.ActiveDocument If .Paragraphs.Count >= 3 Then Set wdRng = .Paragraphs(3).Range wdRng.Copy End If End With 'This line pastes the copied text into a text box 'because that's the default PasteSpecial method for Word text Worksheets("Sheet2").PasteSpecial 'This lines paste the copied text in cell A1 Worksheets("Sheet2").Paste Destination:=Worksheets("Sheet2").Range("A1") Set wdApp = Nothing Set wdRng = Nothing End Sub
After a range is selected, formatting can be applied to it (see Figure 18.10). The following program loops through all the paragraphs of the active document and bolds the first word of each paragraph:
Sub ChangeFormat() Dim wdApp As Word.Application Dim wdRng As Word.Range Dim count As Integer Set wdApp = GetObject(, "Word.Application") With wdApp.ActiveDocument For count = 1 To .Paragraphs.count Set wdRng = .Paragraphs(count).Range With wdRng .Words(1).Font.Bold = True End With Next count End With Set wdApp = Nothing Set wdRng = Nothing End Sub
A quick way of changing the formatting of entire paragraphs is to change the style (see Figures 18.11 and 18.12). The following program finds the paragraph with the NO style and changes it to HA:
Sub ChangeStyle() Dim wdApp As Word.Application Dim wdRng As Word.Range Dim count As Integer Set wdApp = GetObject(, "Word.Application") With wdApp.ActiveDocument For count = 1 To .Paragraphs.count Set wdRng = .Paragraphs(count).Range With wdRng If .Style = "NO" Then .Style = "HA" End If End With Next count End With Set wdApp = Nothing Set wdRng = Nothing End Sub
Bookmarks are members of the Document
, Selection
, and Range
objects. They can help make it easier to navigate around Word. Instead of having to choose words, sentences, or paragraphs, use bookmarks to swiftly manipulate sections of a document.
Bookmarks appear as gray I-bars in Word documents. In Word, click the Microsoft Office Button, go to Word Options, Advanced, Show Document Contents to turn on bookmarks (see Figure 18.13).
After you’ve set up bookmarks in a document, you can use the bookmarks to quickly move to a range. The following code automatically inserts text after four bookmarks that were previously set up in the document. Figure 18.14 shows the results.
Sub UseBookmarks() Dim myArray() Dim wdBkmk As String Dim wdApp As Word.Application Dim wdRng As Word.Range myArray = Array("To", "CC", "From", "Subject") Set wdApp = GetObject(, "Word.Application") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(0)).Range wdRng.InsertBefore ("Bill Jelen") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(1)).Range wdRng.InsertBefore ("Tracy Syrstad") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(2)).Range wdRng.InsertBefore ("MrExcel") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(3)).Range wdRng.InsertBefore ("Fruit Sales") Set wdApp = Nothing Set wdRng = Nothing End Sub
Bookmarks can also be used as markers for bringing in charts created in Excel. The following code links an Excel chart (see Figure 18.15) to the memo:
Sub CreateMemo() Dim myArray() Dim wdBkmk As String Dim wdApp As Word.Application Dim wdRng As Word.Range myArray = Array("To", "CC", "From", "Subject", "Chart") Set wdApp = GetObject(, "Word.Application") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(0)).Range wdRng.InsertBefore ("Bill Jelen") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(1)).Range wdRng.InsertBefore ("Tracy Syrstad") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(2)).Range wdRng.InsertBefore ("MrExcel") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(3)).Range wdRng.InsertBefore ("Fruit & Vegetable Sales") Set wdRng = wdApp.ActiveDocument.Bookmarks(myArray(4)).Range ActiveSheet.ChartObjects("Chart 1").Copy wdRng.PasteAndFormat Type:=wdPasteOLEObject wdApp.Activate Set wdApp = Nothing Set wdRng = Nothing End Sub
You’ve seen how to modify a document by inserting charts and text, modifying formatting, and deleting text. However, a document may contain other items, such as controls, and you can modify those, too.
For the following example, I created a template consisting of text, bookmarks, and Form Field check boxes. (See the note following this paragraph for information on where the Form Fields are hiding in Word.) The bookmarks are placed after the Name and Date fields. The check boxes have all been renamed (right-click the check box, select Properties, and type a new name in the Bookmark field) to make more sense to me, such as chk401k rather than Checkbox5. Save the template.
The Word Form Fields are found on the Controls section of the Developer tab, under the Legacy Tools, as shown in Figure 18.16.
I set up the questionnaire in Excel, allowing the user to enter free text in B1 and B2, but setting up data validation in B3 and B5:B8, as shown in Figure 18.17.
The code goes into a standard module. The name and date go straight into the document. The check boxes use logic to verify whether the user selected Yes or No to confirm whether the corresponding check box should be checked. Figure 18.18 shows a sample completed document.
Sub FillOutWordForm() Dim TemplatePath As String Dim wdApp As Object Dim wdDoc As Object 'Open the template in a new instance of Word TemplatePath = ThisWorkbook.Path & "Word Example.dotx" Set wdApp = CreateObject("Word.Application") Set wdDoc = wdApp.documents.Add(Template:=TemplatePath) 'Place our text values in document With wdApp.ActiveDocument .Bookmarks("Name").Range.InsertBefore Range("B1").Text .Bookmarks("Date").Range.InsertBefore Range("B2").Text End With 'Using basic logic, select the correct form object If Range("B3").Value = "Yes" Then wdDoc.formfields("chkCustYes").CheckBox.Value = True Else wdDoc.formfields("chkCustNo").CheckBox.Value = True End If With wdDoc If Range("B5").Value = "Yes" Then .Formfields("chk401k").CheckBox.Value = True If Range("B6").Value = "Yes" Then .Formfields("chkRoth").CheckBox.Value = True If Range("B7").Value = "Yes" Then .Formfields("chkStocks"). _ CheckBox.Value = True If Range("B7").Value = "Yes" Then .Formfields("chkBonds"). _ CheckBox.Value = True End With wdApp.Visible = True ExitSub: Set wdDoc = Nothing Set wdApp = Nothing End Sub
In Chapter 19, “Arrays,” you will learn how to use multidimensional arrays. Reading data into a multidimensional array, performing calculations on the array, and then writing the array back to a range can dramatically speed up your macros.