Chapter 17: Working with Item Bodies

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

This article is an excerpt from Microsoft Outlook 2007 Programming: Jumpstart for Power Users and Administrators by Sue Mosher from Elsevier (ISBN 978-1-55558-346-0, copyright Elsevier 2008, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

The large text box at the bottom of every standard form exposes what Microsoft Office Outlook 2007 stores as the Body property and, on HTML-format messages and posts, the HTMLBody property. Generically, this information can be referred to as the item body. In earlier chapters, we learned how to access standard, custom, and hidden MAPI properties of Outlook items, but these two standard properties require a more in-depth treatment. Almost every Outlook programmer will work with item bodies at some point, yet the skills needed to work with them successfully go beyond Outlook programming to include Word text handling techniques and even the ability to read text from a file.

Highlights of this chapter include:

  • How to change the format of a message or post

  • How to append or prefix text to an item

  • How to insert text at the user's insertion point

  • Techniques for creating complex HTML-format messages, including inserting hyperlinks and pictures

  • How to create, insert, and remove the user's automatic signature text

Important noteImportant

The code marked as Visual Basic in this chapter is written in Visual Basic for Applications (VBA).

Contents

  • 17.1 Basic item body techniques

  • 17.2 Parsing text from a message body

  • 17.3 Adding text to an item

  • 17.4 Creating a formatted message

  • 17.5 Using WordEditor

  • 17.6 Working with Outlook Signatures

  • 17.7 Summary

17.1 Basic item body techniques

Outlook provides three ways to work with the item body, through three different properties:

  • Through the plain text representation provided by the Body property.

  • Through the tagged HTML representation provided for HTML-format messages and posts through the HTMLBody property.

  • Through the Inspector.WordEditor property, which returns a Word.Document object, even if Word 2007 is not installed, for each item except “sticky notes” and distribution lists.

Table 17.1 Suggested Item Body Techniques

When you want to…

Use this approach…

Parse text from the body of an incoming message.

Parse the contents of the Body property.

Append text to an existing non-message item.

If you don’t care about formatting, modify the Body property.

If you want to preserve formatting, use Inspector.WordEditor.

Insert text at the location the user has selected.

Use Inspector.WordEditor.

Create a highly formatted email message with no embedded images.

Use HTMLBody.

Create a formatted email message with embedded images.

Use Inspector.WordEditor.

NoteNote

Earlier versions of Outlook also supported an Inspector.HTMLEditor object property, but since Internet Explorer is no longer the rendering engine for HTML messages and posts, HTMLEditor is no longer supported.

Table 17.1 suggests when you might want to use which approach.

After a review of some basic item body techniques, we’ll look at examples of each of the scenarios in Table 17.1, and also discuss Outlook signatures.

Any new MailItem created by the Application.CreateItem method uses the default message format set by the user on the Tools | Options | Mail Format dialog. The same is true for any new PostItem. To determine what format the message or post is using, check the value of its BodyFormat property. Table 17.2 lists the possible values.

Message and post items can be in HTML, rich-text (RTF), or plain text format. “Sticky note” items are always plain text. All other items are always RTF.

Table 17.2 OlBodyFormat Constants for the BodyFormat Property

Constant

Literal Value

olFormatHTML

2

olFormatPlain

1

olFormatRichText

3

olFormatUnspecified

0

You can change the format of a message or post by setting its BodyFormat property to a different value. Doing so will lose all formatting if the original message was in HTML or RTF format.

If you set the HTMLBody property for a message or post, the message format changes to HTML and the value of BodyFormat changes to olFormatHTML (2).

If you create and display a message programmatically and automatic signatures are turned on for the user, the displayed message will contain the user’s default signature. If you do not display the message, it will not contain the signature. Later in this chapter, we’ll see code for removing the user’s signature from a message and adding the default signature to an existing message.

The Body, HTMLBody, and WordEditor properties are all subject to the provisions of Outlook “object model guard” security, as discussed in Chapter 10, because item bodies are a significant source of personal details such as email addresses.

17.2 Parsing text from a message body

A common Outlook programming task is to extract information from a message that contains structured text. For example, many Web sites contain forms where site visitors enter information that the Web site, in turn, uses to generate a plain text email message. Such a message likely would have multiple lines, each with a different Label: Data pair, such as:

Name: Flavius J. Littlejohn

Email: flaviusj@turtleflock.net

The code in Listing 17.1 includes a ParseTextLinePair() function you can use to extract the data portion from any such text pair. In this example, the FwdSelToAddr procedure calls ParseTextLinePair() in order to extract the email address from the body of a selected message; it then forwards the message to that address.

The ParseTextLinePair() function uses several built-in functions that we learned about in Chapter 8. It locates the text label (in this example, “Email”) in the source string and then extracts it from the line where it was found, by following these steps:

  1. Locate where search text begins (InStr(strSource, strLabel)).

  2. Locate the end of the line on which the label text appears (InStr(intLocLabel, strSource, vbCrLf)).

  3. Extract the text between the label text and the end of the line (Mid(strSource, intLocLabel + intLenLabel)). Alternatively, if this is the last line, extract the text from the end of the label text until the end of the source text (Mid(strSource, intLocLabel + intLenLabel)).

Listing 17.1. Extract data from a structured text block

(Visual Basic for Applications)

Sub FwdSelToAddr()
    Dim objOL As Outlook.Application
    Dim objItem As Object
    Dim objFwd As Outlook.MailItem
    Dim strAddr As String
    On Error Resume Next
    Set objOL = Application
    Set objItem = objOL.ActiveExplorer.Selection(1)
    If Not objItem Is Nothing Then
        strAddr = ParseTextLinePair(objItem.Body, "Email:")
        If strAddr <> "" Then
            Set objFwd = objItem.Forward
            objFwd.To = strAddr
            objFwd.Display
        Else
            MsgBox "Could not extract address from message."
        End If
    End If
    Set objOL = Nothing
    Set objItem = Nothing
    Set objFwd = Nothing
End Sub

Function ParseTextLinePair _
  (strSource As String, strLabel As String)
    Dim intLocLabel As Integer
    Dim intLocCRLF As Integer
    Dim intLenLabel As Integer
    Dim strText As String
    intLocLabel = InStr(strSource, strLabel)
    intLenLabel = Len(strLabel)
        If intLocLabel > 0 Then
        intLocCRLF = InStr(intLocLabel, strSource, vbCrLf)
        If intLocCRLF > 0 Then
            intLocLabel = intLocLabel + intLenLabel
            strText = Mid(strSource, _
                            intLocLabel, _
                            intLocCRLF - intLocLabel)
        Else
            intLocLabel = _
              Mid(strSource, intLocLabel + intLenLabel)
        End If
    End If
    ParseTextLinePair = Trim(strText)
End Function

Three integer variables assist in this operation:

  • intLocLabel to hold the character position of the label text relative to the entire text body.

  • intLocCRLF to hold the character position of the end of the line where the label text appears.

  • intLenLabel to hold the length of the label text.

This basic text parsing technique is worth studying, because it has many applications, both in Outlook programming and in many other environments.

17.3 Adding text to an item

Another common Outlook programming task is to append or prefix text to an item. Before you undertake this operation, you should consider whether you need to preserve the formatting of text that may already exist in the item, whether you need to format the text you add, and whether you need to position the cursor in a particular location in the item body after inserting the text. That will help determine which of these three methods to use:

  • Modify the Body property (does not preserve formatting).

  • Modify the HTMLBody property (supports formatting).

  • Use Word programming methods to add text through Inspector.WordEditor (supports formatting and cursor positioning).

If the item in question is an HTML-format message or post and you don’t need to position the cursor, whether you use the HTMLBody or WordEditor technique depends largely on your comfort level with those two very different approaches. In other words, which do you know better, HTML tags or Word objects and methods?

17.3.1 Adding text to the Body Property

If you don’t need to preserve the formatting in an item, you can append or prefix text to the item body by working with the Body property. For example, the StampDate macro in Listing 17.2 adds the current date/time and user name to the bottom of the currently open item.

Listing 17.2. Stamp the date and current user on an Outlook item

(Visual Basic for Applications)

Sub StampDate()
    Dim objOL As Outlook.Application
    Dim objNS As Outlook.NameSpace
    Dim objItem As Object
    Dim strStamp As String
    On Error Resume Next
    Set objOL = Application
    Set objItem = objOL.ActiveInspector.CurrentItem
    If Not objItem Is Nothing Then
        Set objNS = objOL.Session
        strStamp = Now & " - " & objNS.CurrentUser.Name
        objItem.Body = objItem.Body & vbCrLf & strStamp
    End If
    Set objOL = Nothing
    Set objNS = Nothing
    Set objItem = Nothing
End Sub

If you wanted the date stamp to appear at the top of the item instead of the bottom, you’d use this variation:

(Visual Basic for Applications)

objItem.Body = Now & " - " & _
  objNS.CurrentUser.Name & vbCrLf & objItem.Body

Any formatting in the item will be lost when you run StampDate, because the Body property provides information only about the plain text representation of the item body.

17.3.2 Adding text to the HTMLBody Property

If you do care about the formatting of the original item body, you need to use a different approach. For an HTML-format message or post, you can provide fully tagged HTML content and insert it into the existing HTML body.

Having fully tagged HTML content is critical. You cannot simply concatenate a text string like the date stamp in Listing 17.1 with the existing HTMLBody content. You must format the string you want to add with full HTML tags and insert it into HTMLBody (note: not append or prefix) in such a way that the structure of the existing body is not compromised. An HTML message or post has, at a minimum, two pairs of tags at the beginning—<html><body>—and end—</body></html>—that define the HTML content. Any text you want to add needs to go between those two tags.

To insert text at the very end of a message, you can replace the </body> tag with your fully tagged HTML content plus a new </body> tag. This code snippet inserts a hyperlink at the end of an existing message (objMsg).

(Visual Basic for Applications)

strLink = "http://www.outlookcode.com"
strLinkText = "Get Outlook code samples here"
strNewText = "<p><a href=" & Chr(34) & strLink & _
             Chr(34) & ">" & strLinkText & "</a></p>"
objMsg.HTMLBody = Replace(objMsg.HTMLBody, "</body>", _
                          strNewText, 1, 1, vbTextCompare)

If you took a look at strNewText with a Debug.Print or MsgBox statement, you’d see that it contains a well-formed HTML hyperlink <a> element.

<a href="http://www.outlookcode.com">Get Outlook code samples here</a>

Another key concept for adding text to an HTML message or post is that the vbCrLf constant that defines a carriage return/linefeed in VBA and VBScript has no meaning in HTML. In the example above, the code inserts the hyperlink as a new paragraph enclosed inside a pair of <p></p> tags. To insert a single line break, use a <br> tag. This code snippet replaces the vbCrLf instances in a text string with double line breaks.

(Visual Basic for Applications)

strNewText = Replace(strOldText, vbCrLf, "<br><br>")

Listing 17.3 shows another practical application of the </body> replacement technique, stamping the date as a separate paragraph at the end of an HTML-format message or post. Compare with Listing 17.2.

The two main differences between Listing 17.2 and Listing 17.3 are the way the strStamp date stamp string is constructed—one with <p></p> tags and one with vbCrLf—and the use of the Replace() function to insert the new content immediately before the existing </body> tag. Again, the trick is to replace the </body> tag with a new string constructed from the date stamp followed by </body>.

Prefixing the existing content with a date stamp is a little trickier than appending, because the <body> tag may contain attribute settings. In other words, the tag may be something other than just plain <body>. It may be as verbose as <body lang=EN-US link=blue vlink=purple>. To handle that scenario, you need to find the entire tag, using the same kind of text parsing with Instr() and Mid() that you saw in the ParseTextLinePair() function in Listing 17.1.

Listing 17.3. Stamp the date and current user on an HTML-format message or post

(Visual Basic for Applications)

Sub StampDateHTML()
    Dim objOL As Outlook.Application
    Dim objNS As Outlook.NameSpace
    Dim objItem As Object
    Dim strStamp As String
    On Error Resume Next
    Set objOL = Application
    Set objItem = objOL.ActiveInspector.CurrentItem
    If Not objItem Is Nothing Then
        If objItem.BodyFormat = olFormatHTML Then
            Set objNS = objOL.Session
            strStamp = "<p>" & Now & " - " & _
                       objNS.CurrentUser.Name & "</p>"
            objItem.HTMLBody = Replace(objItem.HTMLBody, _
                               "</body>", _
                               strStamp & "</body>", _
                               , , vbTextCompare)
        End If
    End If
    Set objOL = Nothing
    Set objNS = Nothing
    Set objItem = Nothing
End Sub

Once you have the entire tag, you use the same Replace() technique as in Listing 17.3. Only this time, you replace the initial <body> tag (with all its attributes) with a new string consisting of the full <body> tag followed by the date stamp.

(Visual Basic for Applications)

strHTMLBody = objItem.HTMLBody
intTagStart = InStr(1, strHTMLBody, "<body", _
  vbTextCompare)
intTagEnd = InStr(intTagStart + 5, strHTMLBody, ">")
strBodyTag = _
  Mid(strHTMLBody, _
      intTagStart, intTagEnd - intTagStart + 1)
objItem.HTMLBody = _
  Replace(strHTMLBody, strBodyTag, strBodyTag & strStamp)

To insert text into the middle of an HTML-format message or post, you could use the same text-parsing technique to locate the tag or text where you want to place the insertion, then replace that tag or text with a string that consists of the original content concatenated with your new content. Later in this chapter, we’ll see a simple application of that technique, as we customize a previously saved message template to tailor it to the current recipient.

If you know a bit about HTML, you can start embellishing such text insertions with formatting. For example, these statements build a date stamp string that will appear in red, bold, Arial text.

(Visual Basic for Applications)

strStyle = "'font-family:" & Chr(34) & _
           "Arial" & Chr(34) & ";color:red'"
strStamp = "<p><b>" & _
           "<span style=" & strStyle & ">" & _
           Now & " - " & objNS.CurrentUser.Name & _
           "</span></b></p>"

The bold formatting is handled by the <b></b> tags while the font formatting is handled by the <span></span> tag containing a style attribute. We can’t cover HTML in detail in this book, but tutorials abound on the Internet. Another good learning tool is to create an email message with some other mail program such as Outlook Express, Windows Mail, or even a Web-based mail service such as Gmail. Send the message to yourself, and then examine its HTML content by clicking in the body of the message and choosing View Source or by looking at the value of its HTMLBody property.

The third method for inserting text requires the use of the Word.Document object returned by the Inspector.WordEditor property. This is the most versatile method, because it allows you not only to preserve formatting in any type of Outlook item, but also to insert text where the user has placed the cursor. We cover it later in this chapter, in the section on WordEditor.

17.4 Creating a formatted message

The previous sections have been concerned with reading text from a message and inserting text, with some simple formatting options. Sometimes, though, you have a more complicated task—creating a complete message with complex formatting. In almost all cases, this will be an HTML-format message, since RTF messages work only when the recipient has Outlook, and you usually don’t know what mail application the recipient uses. We’ll look at two techniques in this chapter:

  • Reading HTML content from a saved file

  • Adding customized text to a boilerplate message created from a saved Outlook template file

You can also build an HTML message on the fly, element by element. Skip ahead to Listings 18.1 and 18.2 in the next chapter to see examples that build an HTMLBody that contains a table, one reporting on the user’s available address lists, the second example listing colleagues in the same department along with their contact information.

17.4.1 Creating an HTML-format message from a file

Chapter 8 explained how to use FileSystemObject to work with folders and files. A common application of those techniques is to use the data in a saved HTML file to create a new HTML message. This is a particularly good strategy if you want to use a dedicated HTML editor to create, for example, a newsletter.

NoteNote

Outlook 2007’s support for cascading style sheets and various HTML elements has changed substantially from that in previous versions, now that Word is both the editor and the rendering engine. To learn what is and is not supported in Outlook 2007 HTML-format messages, read the article, Word 2007 HTML and CSS Rendering Capabilities in Outlook 2007 (Part 1 of 2) on MSDN.

The CreateHTMLMsg() procedure in listing 17.4 is a VBA function that reads the text from an existing .htm file and creates and displays a new Outlook message. Recall that you need a reference to the Microsoft Scripting Runtime library to be able to declare objFSO as Scripting.FileSystemObject. Use code like this to call CreateHTMLMsg and display the newly created message.

(Visual Basic for Applications)

Set objMsg = CreateHTMLMsg("C:\MyNewsletter.htm")
objMsg.Display

Listing 17.4. Create a message from an HTM file

(Visual Basic for Applications)

Function CreateHTMLMsg(fileHTML As String) _
  As Outlook.MailItem
    Dim objOL As Outlook.Application
    Dim objMsg As Outlook.MailItem
    Dim objFSO As Scripting.FileSystemObject
    Dim objStream As Scripting.TextStream
    Dim strHTMLFile As String
    On Error Resume Next
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    If objFSO.FileExists(fileHTML) Then
        Set objOL = Application
        Set objMsg = objOL.CreateItem(olMailItem)
        Set objStream = objFSO.OpenTextFile(fileHTML, _
                                            ForReading)
        objMsg.HTMLBody = objStream.ReadAll
    End If
    Set CreateHTMLMsg = objMsg
    Set objOL = Nothing
    Set objMsg = Nothing
    Set objFSO = Nothing
    Set objStream = Nothing
End Function

Once you have the message returned by CreateHTMLMsg(),you could modify its contents further, using the other methods in this chapter, before displaying it.

When you need to render an HTML message in the browser

Because Outlook 2007 has shifted the rendering engine for HTML-format messages from Internet Explorer to Word, HTML tags like <script> and <form> are no longer supported. Earlier versions of Outlook ignored those tags by default. At the same time, though, they supported a View | View in Internet Zone command that would allow users to see the message, but in a less secure fashion. Outlook 2007 doesn’t support the concept of different security zones for viewing messages, but it does still have a command that will allow a user to view a message in their default Internet browser—the View in Browser command under Other Actions in the Actions group.

To direct the recipient of a message to use the View in Browser command to see the message content that Outlook 2007 blocks, you can include a special element in your HTML code. To display directions to Outlook 2007 users at the top of the message, put code like this just after the <body> tag.

<!--[if gte mso 12]><BR>To see the submission button using Outlook 2007, click the <STRONG>Other Actions</STRONG> button, then <STRONG>View in Browser</STRONG>.<BR><![endif]-->

The <!--[if gte mso 12]> tag in effect says: Show this tag’s content only if the rendering engine is greater than or equal to Office “12”—that is, only if it is Office 2007 or later.

17.4.2 Creating a message from a boilerplate template

Another common Outlook message scenario is replying to an incoming message with a standard response. For example, you might want to reply to requests for product literature with an attractive, personalized message that includes the literature as one or more attached files. This is not a task that you can accomplish with a published custom form. The solution is to use a saved .oft form template file constructed with text that your code can easily find and replace with personalized information. That text consists of “tokens” in the message body, each beginning and ending with % or some other character that makes them easy to distinguish from the actual message text.

To implement this solution, start by following these steps to create a boilerplate response message:

  1. Create a new HTML-format message, and type in the fully formatted message body you want the recipient to see, including your signature, if desired. Don’t forget to give the message a subject and attach any desired files.

  2. In the message body, type in a “token” wherever you want a customizable text “field,” each token starting and ending with %. For example, if you want to address the sender of the original message by name, put Dear %sender% in the message body. Add more tokens where you want other fields that can be personalized.

  3. Save the message as an .oft file.

Figure 17.1. Use a "tokenized" boilerplate message to generate customized replies

Use tokens in a boilerplate message

Once you have saved the response message, the next step is to write a VBA procedure that creates a new message from the .oft file, using the Application.CreateItemFromTemplate method. That code will need to replace each token with specific text, using the Replace() function. The To address for the new message comes from the SenderEmailAddress of the original that you want to reply to.

Figure 17.1 shows an example of such a message, ready for saving as an .oft file. The code to generate a reply using that template is in Listing 17.5. Call the InquiryReply procedure by passing it an Outlook message. For example, you could use the GetCurrentItem() function from Listing 15.5.

(Visual Basic for Applications)

Call InquiryReply(GetCurrentItem())

As you can see in Figure 17.1, the reply template has two tokens—%sender% and %project_type%. The InquiryReply procedure fills in the %project_type% token from the result of an InputBox() statement and attempts to fill in the %sender% token with the first name of the sender of the original message. Just in case the sender name wasn’t available or wasn’t parsed correctly, the code uses the Word editor’s search capability to highlight the %sender% token (or the text that replaced it) in the displayed reply message. (More on the Word editor is coming up shortly.)

What if you want to include the original sender’s message in your reply? That’s possible, too, and we’ll look at it later in the chapter, after you’ve learned how to move around in the WordEditor.

Listing 17.5. Create a reply from a tokenized boilerplate message

(Visual Basic for Applications)

Sub InquiryReply(msg)
    Dim objOL As Outlook.Application
    Dim objReply As Outlook.MailItem
    Dim objDoc As Object
    Dim strSender As String
    Dim arr() As String
    Dim strProject As String
    Dim strHTML As String
    On Error Resume Next 
    Set objOL = msg.Application
    Set objReply = _
      objOL.CreateItemFromTemplate _
        ("C:\Data\inquiry response.oft")
    objReply.To = msg.SenderEmailAddress
    strSender = msg.SenderName
    strProject = InputBox("Enter project type:", _
                          "Replace %project_type%", _
                          "custom form")
    strHTML = Replace(objReply.HTMLBody, _
                      "%project_type%", strProject)
    If strSender <> msg.SenderEmailAddress Then
        arr = Split(strSender, " ")
        strSender = arr(0)
        strHTML = Replace(strHTML, "%sender%", strSender)
    Else
        strSender = "%sender%"
    End If
    objReply.HTMLBody = strHTML
    objReply.Display
    Set objDoc = objReply.GetInspector.WordEditor
    objDoc.Windows(1).Selection.Find.ClearFormatting
    objDoc.Windows(1).Selection.Find.Execute strSender
    Set objOL = Nothing
    Set objReply = Nothing
    Set objDoc = Nothing
End Sub

17.5 Using WordEditor

The Outlook object model itself provides no direct way to determine the position of the cursor in an item body. However, since the editor for every item body (except on “sticky notes” and distribution lists) is a special version of Microsoft Word, you can use Word techniques not only to add text at the insertion point, but also to add formatted text anywhere in the item, or even to add a picture. To use these techniques in Outlook VBA code, use the Tools | References command to add a reference to the Microsoft Word 12.0 Object Library.

NoteNote

In earlier versions of Outlook, the WordEditor was available only for messages and posts and only if the user had configured Word as the email editor. Since Word is the only editor in Outlook 2007 (except for NoteItem and DistListItem objects), it is available not just for messages and posts, but also for appointments, contacts, tasks, and journal entries. It even works if only Outlook 2007 is installed, and not Word 2007.

As an initial example of how to invoke the Word editor programmatically, using the Inspector.WordEditor method, Listing 17.6 builds on the earlier examples in Listings 17.2 and 17.3 and inserts a date stamp at the top of the currently displayed item, with a blank line following it. It also positions the cursor on the blank line, so that if the focus is on the message body control, the user can begin typing right after the date stamp.

Listing 17.6. Insert text and reposition the cursor

(Visual Basic for Applications)

Sub StampDateDoc()
    Dim objOL As Outlook.Application
    Dim objNS As Outlook.NameSpace
    Dim objDoc As Word.Document
    Dim objSel As Word.Selection
    Dim strStamp As String
    On Error Resume Next
    Set objOL = Application
    If objOL.ActiveInspector.EditorType = olEditorWord Then
        Set objDoc = objOL.ActiveInspector.WordEditor
        Set objNS = objOL.Session
        strStamp = Now & " - " & objNS.CurrentUser.Name
        Set objSel = objDoc.Windows(1).Selection
        objSel.Move wdStory, -1
        objDoc.Characters(1).InsertBefore _
          strStamp & vbCrLf & vbCrLf
        objSel.Move wdParagraph, 1
    End If
    Set objOL = Nothing
    Set objNS = Nothing
End Sub

17.6 builds on the earlier examples in Listings 17.2 and 17.3 and inserts a date stamp at the top of the currently displayed item, with a blank line following it. It also positions the cursor on the blank line, so that if the focus is on the message body control, the user can begin typing right after the date stamp.

Two key objects from the Word object model can be derived from Document object that the Inspector.WordEditor method returns. If objDoc is the object variable representing the Document, then the expression objDoc.Application returns a Word Application object. The other key object is the Selection object (not to be confused with ActiveExplorer.Selection from the Outlook object model), which represents the text that is currently highlighted in the Outlook item. If no text is highlighted, it represents the cursor position. Use this syntax to return a Selection object from an Outlook item’s Document object.

(Visual Basic for Applications)

Set objSel = objDoc.Windows(1).Selection

The Selection object includes a number of methods, including Move, to reposition the insertion point. We’ll look at Move in more detail in the next section.

To insert text at the current insertion point, use the Selection.InsertBefore method. (As you might expect, there is also a Selection.InsertAfter method.) In this code snippet, strText is a variable holding the text you want to insert.

(Visual Basic for Applications)

Set objOL = Application
Set objDoc = objOL.ActiveInspector.WordEditor
Set objSel = objDoc.Windows(1).Selection
objSel.InsertBefore strText

You can even format the inserted text. The Selection expands to include the inserted text whenever you use Selection.InsertBefore or Selection.InsertAfter. Since the inserted text is selected automatically, it is easy to use the Selection.Font object to change the appearance of the new text. This snippet inserts text and then makes it bold, red, Arial.

(Visual Basic for Applications)

Set objSel = objDoc.Windows(1).Selection
With objSel
    .Collapse wdCollapseStart
    .InsertBefore strText
    .Font.Name = "Arial"
    .Font.Bold = True
    .Font.Color = wdColorRed
End With
NoteNote

Most of the time, you will want to collapse the selection to the insertion point, using the Selection.Collapse method, before inserting the text. If you don’t do that, your code will replace any text that the user has selected. The Selection.Move method automatically collapses the insertion point.

To learn more about working with formatted text in Word, a good strategy is to open Word 2007, create a new document, and turn on the Word macro recorder. (The Word macro recorder is not available in an Outlook message or other item.) Much of the code it produces uses a Selection object. The code you’ve seen in this section shows how to return the Selection object for an Outlook item using Inspector.WordEditor and the Document.Application.Selection object. You should be able to adapt almost any code that the Word macro recorder produces to a Word Selection object derived from an Outlook item.

17.5.1 Moving around in the Word Editor

The key to moving the cursor position—also known as the insertion point—inside the Word editor is found in these statements from Listing 17.6.

(Visual Basic for Applications)

Set objDoc = objOL.ActiveInspector.WordEditor
Set objSel = objDoc.Windows(1).Selection
objSel.Move wdStory, -1
objSel.Move wdParagraph, 1

The Selection.Move method collapses the selection and then moves it a specified distance. The first parameter for Move is a WdUnits constant (from the Word object model) defining how big a step to take during the move. The second parameter is an integer defining how many steps to take. If the number is positive, the insertion point is collapsed to the end of the selection and then moved forward the specified number of units. If the number is negative, the insertion point collapses to the start of the selection and moves backwards the specified number of units. Table 17.3 lists the WdUnits constants that are useful in moving through email messages.

Thus, to move the insertion point to the beginning of a message, assuming you already have a Selection object (objSel), use:

(Visual Basic for Applications)

objSel.Move wdStory, -1

To move the insertion point to the end of the message, use:

(Visual Basic for Applications)

objSel.Move wdStory, 1

To move the insertion point to the cell in the second column of the third row of the first table in a message, use:

(Visual Basic for Applications)

objSel.Move wdStory, -1
objSel.Move wdTable, 1
objSel.Move wdRow, 2
objSel.Move wdCell, 1
NoteNote

Word also supports a Selection.GoTo method to reposition the insertion point, but it raises an error if Word 2007 is not installed. Since Move and related Selection methods work even without Word 2007, you should rely on them, not GoTo.

In addition, the Selection object supports methods like MoveUp and MoveDown that can be used to extend the selection to cover additional text. You can look them up in the object browser.

Table 17.3 Word WdUnits Constants for Moving the Insertion Point

Unit

Constant

Value

Story (= the entire message)

wdStory

6

Paragraph

wdParagraph

4

Line

wdLine

5

Sentence

wdSentence

3

Word

wdWord

2

Character

wdCharacter

1

Table

wdTable

15

Row

wdRow

10

Column

wdColumn

9

Cell

wdCell

12

The Move method can return an integer representing the number of units moved. For example, given this statement:

(Visual Basic for Applications)

intMoved = objSel.Move(wdParagraph, 3)

if intMoved is less than 3, that means the insertion point is now at the end of the message, because it couldn’t move forward three whole paragraphs.

Another useful technique for moving around in the Word editor window is to invoke the Find command programmatically. Earlier in Listing 17.5, you saw these statements that find and highlight the text in a reply message showing the original sender’s name, so that the user can confirm and correct it as needed.

(Visual Basic for Applications)

Set objDoc = objReply.GetInspector.WordEditor
objDoc.Windows(1).Selection.Find.ClearFormatting
objDoc.Windows(1).Selection.Find.Execute strSender

Always use the Selection.Find.ClearFormatting method to clear any previously used option to search for text with specific formatting. The example above is the simplest implementation of Find.Execute to search forward in a document for specific text. The Find.Execute method also supports many optional parameters, which you can look up in the object browser, including those that allow you to replace text or control the direction of the search.

17.5.2 Example: Boilerplate reply that includes incoming text

Back in the discussion of Listing 17.5, to create a reply message from a saved boilerplate .oft file, we said that it’s possible to include the original message text in the reply message, just as a manually created Outlook reply would do. Now that you know how to use the WordEditor, we can walk through the steps in that process:

  1. Generate a reply to the original message.

  2. Create a new message from the boilerplate .oft file.

  3. Copy the text from the reply body to the end of the new message, using Word methods.

  4. The InquiryReplyWithOrig procedure in Listing 17.7 builds on the InquiryReply procedure Listing 17.5 to add the code to generate a reply and copy its content to the message created from the .oft file.

Listing 17.7 demonstrates several other useful methods in the Word Selection object:

  • MoveEnd to expand the end point of the Selection object so that more text is selected; these statements locate the From: text in the reply, select all the text from that point to the end of the reply, then copy that text to the Windows clipboard.

    (Visual Basic for Applications)

    With objSel
        .Find.Execute “From:”
        .Collapse wdCollapseStart
        .MoveEnd WdUnits.wdStory, 1, True
        .Copy
    End With
    
  • InlineShapes to add a shape to the message, in this case, a horizontal line.

  • PasteAndFormat to paste the text from the clipboard into the new message, preserving its formatting.

The next two sections look at other techniques you’re likely to use—inserting hyperlinks and pictures.

TipTip

For another example of the PasteAndFormat method, Listing 20.1 includes a CopyFormattedBody subroutine that copies a complete item body, including formatting, from one Outlook item to another.

Listing 17.7. Include the original sender’s message with a boilerplate reply

(Visual Basic for Applications)

Sub InquiryReplyWithOrig(msg)
    Dim objOL As Outlook.Application
    Dim objReply As Outlook.MailItem
    Dim objOrigReply As Outlook.MailItem
    Dim objDoc As Word.Document
    Dim objDocOrigReply As Word.Document
    Dim objSel As Word.Selection
    Dim strSender As String
    Dim arr() As String
    Dim strProject As String
    Dim strHTML As String
    Dim f As Boolean
    Set objOL = msg.Application
    Set objReply = _
      objOL.CreateItemFromTemplate _
        ("C:\Data\inquiry response.oft")
    objReply.To = msg.SenderEmailAddress
    strSender = msg.SenderName
    strProject = InputBox("Enter project type:", _
                          "Replace %project_type%", _
                          "custom form")
    strHTML = Replace(objReply.HTMLBody, _
                      "%project_type%", strProject)
    If strSender <> msg.SenderEmailAddress Then
        arr = Split(strSender, " ")
        strSender = arr(0)
        strHTML = Replace(strHTML, "%sender%", strSender)
    Else
        strSender = "%sender%"
    End If
    objReply.HTMLBody = strHTML
    Set objOrigReply = msg.Reply
    Set objDoc = objReply.GetInspector.WordEditor
    Set objDocOrigReply = objOrigReply.GetInspector.WordEditor
    Set objSel = objDocOrigReply.Windows(1).Selection
    With objSel
        .Find.Execute "From:"
        .Collapse wdCollapseStart
        .MoveEnd WdUnits.wdStory, 1
        .Copy
    End With
    Set objSel = objDoc.Windows(1).Selection
    With objSel
        .Move wdStory, 1
        .InlineShapes.AddHorizontalLineStandard
        .PasteAndFormat wdFormatOriginalFormatting
        .Move wdStory, -1
        .Find.ClearFormatting
        .Find.Execute strSender
    End With
    objReply.Display
    Set objOL = Nothing
    Set objReply = Nothing
    Set objDoc = Nothing
End Sub

Back in the discussion of Listing 17.5, to create a reply message from a saved

Earlier in this chapter, we saw how to insert a hyperlink at the end of an HTML-formatted message using the </body> tag replacement technique. That technique won’t work if you want to insert a hyperlink in an RTF message or an item other than a message or post. It also can’t help you insert a hyperlink at the current insertion point. For those scenarios, you need to use Inspector.WordEditor and the Document.Hyperlinks.Add method from the Word object model. The Hyperlinks.Add method uses this syntax.

(Visual Basic for Applications)

objDoc.Hyperlinks.Add(Anchor, Address, SubAddress, _
                      ScreenTip, TextToDisplay, Target)

The Anchor parameter is the only required argument. It needs to be an object representing the text or image that you want to mark as a hyperlink; it can also be the Selection.Range object representing a collapsed insertion point. The actual URL for the link is passed as the Address parameter. Pass the display text for the link with the TextToDisplay parameter. The other three parameters do not apply to hyperlinks in email messages.

Compare this code to insert a hyperlink into an existing message (objMsg) with the corresponding code snippet in Section 17.3.2.

(Visual Basic for Applications)

strLink = "http://www.outlookcode.com"
strLinkText = "Get Outlook code samples here"
Set objInsp = objMsg.GetInspector
Set objDoc = objInsp.WordEditor
Set objSel = objDoc.Windows(1).Selection
If objMsg.BodyFormat <> olFormatPlain Then
    objDoc.Hyperlinks.Add objSel.Range, strLink, _
                          "", "", strLinkText, ""
Else
    objSel.InsertAfter strLink
End If

Notice that the syntax for inserting a link into a plain text message is different from that for HTML and RTF messages. For a plain text message, you should insert only the URL.

17.5.4 Inserting pictures

If you want a picture to appear in the body of an email message, rather than as an attachment, you should send it as an embedded picture, not as an <img> HTML tag with a link to an external URL. Outlook 2003 and Outlook 2007 block external content by default, as do a growing number of other mail programs.

Inserting an embedded picture is very similar to inserting a hyperlink, except that the method is Selection.InlineShapes.AddPicture, instead of Document.Hyperlinks.Add. The InlineShapes.AddPicture method uses this syntax.

(Visual Basic for Applications)

objSel.AddPicture(FileName, LinkToFile, _
                  SaveWithDocument, Range)

where FileName is the name of the file that contains the picture. For Outlook items, the LinkToFile and SaveWithDocument parameters should always be False and True respectively. Omit the optional Range parameter to insert the picture at the current insertion point.

Compare this code snippet for inserting a picture to the one in the previous section for inserting a hyperlink.

(Visual Basic for Applications)

strFile = "C:\Pictures\logo.gif"
Set objInsp = objMsg.GetInspector
Set objDoc = objInsp.WordEditor
Set objSel = objDoc.Windows(1).Selection
If objMsg.BodyFormat <> olFormatPlain Then
    objSel.InlineShapes.AddPicture strFile, False, True
End If

What about combining a picture with a hyperlink? The InlineShapes.AddPicture method returns an InlineShape object. You can use the InlineShape.Range object property as the anchor for a hyperlink instead of the Selection.Range object used by the hyperlink sample code in the previous section.

(Visual Basic for Applications)

Set objShape = objSel.InlineShapes.AddPicture _
                 (strFile, False, True)
objDoc.Hyperlinks.Add objShape.Range, strLink, _
                          "", "", strLinkText, ""

We don’t have room in this book to go into detail on all the functionality available with the Word objects available to you from WordEditor, but you can explore them on your own with the object browser and Word’s macro recorder (which is available only in Word documents, not in Outlook messages).

17.6 Working with Outlook Signatures

As in previous versions, Outlook 2007 offers extensive support for personal email signatures, which are stored as .htm, .rtf, and .txt files to support the three different message formats. Users may create one or more signatures and have Outlook apply them automatically or insert signatures manually.

NoteNote

One difference between Outlook 2007 and earlier versions is that inserting a signature in Outlook 2007 always replaces any existing signature in the message. Thus, you cannot use signatures in Outlook 2007 as a way of inserting multiple blocks of text into a single message. Instead, you can use the Word insertion methods described earlier in the chapter; the section on inserting the default signature provides another example.

The replacement for the AutoText feature in earlier versions is Quick Parts, found on the ribbon on the Insert tab, in the Text group and stored in the Normalemail.dotm template. To insert a quick part named “Sales Inquiry,” use this code:

(Visual Basic for Applications)

Set objDoc = objOL.ActiveInspector.WordEditor
Set objWord = objDoc.Application
Set objSel = objDoc.Windows(1).Selection
Set objETemp = objWord.Templates(1)
Set colBlocks = objETemp.BuildingBlockEntries
colBlocks("Sales Inquiry").Insert _
  objSel.Range, True

The three key signature tasks that we’ll cover are creating a signature programmatically, inserting the user’s default signature into a message, and removing a signature that has already been inserted.

17.6.1 Creating a signature

As part of the task of creating a new signature for the current user, if the user has an Exchange mailbox, we can incorporate company contact information into the signature. For this task, we will make use of the new ExchangeUser object that Outlook 2007 introduces. The code in Listing 17.8 is written in VBScript, but it’s structured differently from the VBScript for Outlook forms. That’s because this script is intended to be run as part of a login or as an independent script stored in a .vbs file. (You saw an example of this technique earlier in Listing 7.8.) The code that actually creates the signature is in the CreateSignature subroutine, which takes a Namespace object, representing the Outlook session where the user has logged in, as its sole parameter.

Listing 17.8. Script to create and format a new default signature

Dim objOL                   ' As Outlook.Application
Dim objNS                   ' As Outlook.NameSpace
Dim blnWeStartedOutlook     ' As Boolean
Const olFolderInbox = 6
On Error Resume Next
Set objOL = GetObject(, "Outlook.Application")
If objOL Is Nothing Then
    Set objOL = CreateObject("Outlook.Application")
    Set objNS = objOL.GetNamespace("MAPI")
    objNS.Logon "", "", True, True
    ' objNS.Logon "Outlook Settings", "", False, True
    blnWeStartedOutlook = True
Else 
    Set objNS = objOL.GetNamespace("MAPI")
    objNS.Logon "", "", False, False
End If
If Not objNS.GetDefaultFolder(olFolderInbox) Is Nothing Then
    Call CreateSignature(objNS)
Else
    MsgBox "Could not start Outlook to set up signature" 
End If
If blnWeStartedOutlook Then 
    objNS.Logoff
    objOL.Quit
End If
Set objOL = Nothing
Set objNS = Nothing

Sub CreateSignature(objNS)
    Dim objMsg              ' As Outlook.MailItem
    Dim objDoc              ' As Word.Document
    Dim objSel              ' As Word.Selection
    Dim objSig              ' As Word.EmailSignature
    Dim colSig              ' As Word.EmailSignatureEntries
    Dim objExUser           ' As Outlook.ExchangeUser
    Dim objUser             ' As Outlook.AddressEntry
    Dim strSig              ' As String
    Dim objInsp             ' As Outlook.Inspector
    Const olmailitem = 0
    Const wdCollapseEnd = 0
    Const wdStory = 6
    Const olDiscard = 1
    Const olMinimized = 1 
    Set objUser = objNS.CurrentUser.AddressEntry
    Set objMsg = objNS.Application.CreateItem(olmailitem)
    objMsg.Display
    Set objInsp = objMsg.GetInspector
    objInsp.WindowState = olMinimized
    Set objDoc = objInsp.WordEditor
    Set objSel = objDoc.Application.Selection
    With objSel
        .Move wdStory, -1
        .InsertAfter "--" & vbCrLf & Space(3)
        .Collapse wdCollapseEnd
        .InsertAfter objUser.Name
        .Font.Bold = True
        .InsertAfter "  "
        .Collapse wdCollapseEnd
    End With
    If objUser.AddressEntryUserType = _
      olExchangeUserAddressEntry Then
        Set objExUser = objUser.GetExchangeUser
        If objExUser.Department <> "" Then
            strSig = vbCrLf & Space(3) & objExUser.Department
        End If
        If objExUser.CompanyName <> "" Then
            strSig = strSig & vbCrLf & Space(3) & _
                     objExUser.CompanyName
        End If
            If objExUser.BusinessTelephoneNumber <> "" Then
            strSig = strSig & vbCrLf & Space(3) & _
                     objExUser.BusinessTelephoneNumber
        End If
        With objSel
            .InsertAfter objExUser.PrimarySmtpAddress
            .Font.Bold = False
            objDoc.Hyperlinks.Add objSel.Range, _
              "mailto:" & objExUser.PrimarySmtpAddress
            .Collapse wdCollapseEnd
            .InsertAfter strSig
        End With
    Else
        With objSel
            .InsertAfter objUser.Address
            .Font.Bold = False
            objDoc.Hyperlinks.Add objSel.Range, _
                                  "mailto:" & objUser.Address
            .Collapse wdCollapseEnd
        End With
    End If
    objSel.InsertAfter vbCrLf
    objSel.MoveStart wdStory, -1
    objSel.Font.Color = wdColorBlack
    Set objSig = _
      objDoc.Application.EmailOptions.EmailSignature
    Set colSig = objSig.EmailSignatureEntries
    colSig.Add objUser.Name, objSel.Range
    objSig.NewMessageSignature = objUser.Name
    objSig.ReplyMessageSignature = objUser.Name
    objInsp.Close olDiscard
    Set objMsg = Nothing
    Set objDoc = Nothing
    Set objSel = Nothing
    Set objSig = Nothing
    Set colSig = Nothing
    Set objExUser = Nothing
    Set objUser = Nothing
    Set objInsp = nothing 
End Sub

As with the other item body manipulation techniques in this chapter, the CreateSignature subroutine uses Word methods to insert and format text. From the Namespace.CurrentUser.AddressEntry object, the code can add information about the user to the signature. If the user is an Exchange user, the GetExchangeUser method provides access to properties like Department, BusinessTelephoneNumber, and PrimarySmtpAddress as stored in the Global Address List on the server, for example:

Set objExUser = objUser.GetExchangeUser
If objExUser.Department <> "" Then
    strSig = vbCrLf & Space(3) & objExUser.Department
End If

Surprisingly, the objects used to actually set the default signature for the new messages and replies/forwards are Word objects, not Outlook objects. Thus, the code accesses the signature options through the parent Application object of the Word Document where the signature text is being built.

Set objSig = _
  objDoc.Application.EmailOptions.EmailSignature
Set colSig = objSig.EmailSignatureEntries
colSig.Add objUser.Name, objSel.Range
objSig.NewMessageSignature = objUser.Name
objSig.ReplyMessageSignature = objUser.Name

Through the user interface, the user can also set a different automatic signature for each mail account. Those per-account signature settings cannot be programmatically managed through Outlook or Word objects, but are buried in the registry entries for the user’s Outlook mail profile.

17.6.2 Inserting the default signature

The insertion technique used in Listing 17.7 can also be used to add the user’s default automatic signature to an existing message. The technique consists of creating a new message, copying the content, then pasting that content into the existing message. This VBA code snippet creates a new message and then copies the signature.

(Visual Basic for Applications)

Set objMsg = Application.CreateItem(olMailItem)
Set objSigDoc = objMsg.GetInspector.WordEditor
Set objSel = objSigDoc.Windows(1).Selection
With objSel
    .Collapse wdCollapseStart
    .MoveEnd WdUnits.wdStory, 1
    .Copy
End With

You can then use the Selection.PasteAndFormat method, as shown in Listing 17.7, to paste the signature into the desired location in another Outlook message.

17.6.3 Removing signature text

What if you want to do the opposite—remove a signature that Outlook inserts automatically for the user? The key to that task is knowing that the signature is contained in a hidden Word bookmark named _MailAutoSig. The DeleteSig procedure in Listing 17.9 locates that bookmark in a message body, selects it, and then deletes the content of the selection.

Listing 17.9. Use a Word bookmark to delete an automatic signature

(Visual Basic for Applications)

Sub TestDeleteSig()
    Dim objMsg As Outlook.MailItem
    Set objMsg = Application.CreateItem(olMailItem)
    objMsg.Display
    Call DeleteSig(objMsg)
    Set objMsg = Nothing
End Sub

Sub DeleteSig(msg As Outlook.MailItem)
    Dim objDoc As Word.Document
    Dim objBkm As Word.Bookmark
    On Error Resume Next
    Set objDoc = msg.GetInspector.WordEditor
    Set objBkm = objDoc.Bookmarks("_MailAutoSig")
    If Not objBkm Is Nothing Then
        objBkm.Select
        objDoc.Windows(1).Selection.Delete
    End If
    Set objDoc = Nothing
    Set objBkm = Nothing
End Sub

If you wanted to replace an existing signature with another one, you could combine the removal technique in Listing 17.9 with the insertion technique in the previous section.

17.7 Summary

As Outlook items are the core of the application’s data, the body of each item is the heart of the item. Now that Word is the editor for both mail messages and other Outlook items, you can take advantage of its text manipulation and formatting techniques in most Outlook items. Inserting hyperlinks and pictures also works through the Document object returned by the Inspector.WordEditor method. Inserting text, hyperlinks, and pictures are all possible through the WordEditor object.

Among the things we’ve learned in this chapter are three different ways to insert a date stamp (or other text) into an item, several methods for creating complex HTML-format messages, and techniques for creating, inserting, and removing a signature. A key reusable routine introduced in this chapter is the ParseTextLinePair() function to extract data from a structured text block.

Additional Resources