Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles

This month we take a look at FxCop and other tools that enforce your design rules, along with jQuery.

Scott Mitchell

MSDN Magazine December 2008

...

Read more!

The heart of Windows Workflow Foundation is its declarative programming model. Here are some best practices to consider when using WF to realize software solutions in the real world.

Josh Lane

MSDN Magazine December 2008

...

Read more!

Choosing the right design pattern for your ASP.NET Web application can help you achieve the separation of concerns between your presentation layer and the layers beneath it.

Dino Esposito

MSDN Magazine December 2008

...

Read more!

This month Scott looks at improving development skills, writing regular expressions, a web scheduling control and a SQL tips blog.

Scott Mitchell

MSDN Magazine November 2008

...

Read more!

The CLR team takes a look inside the System.Globalization namespace to explain how to handle data formats for proper localization and globalization.

Melitta Andersen

MSDN Magazine November 2008

...

Read more!

Also by this Author

Often, IM conversations contain important information you'd like to keep and reuse. Fortunately, MSN Messenger 6.2 has a feature to keep a conversation history permanently in XML format. This article shows you how to leverage that conversation history by consolidating IM exchanges so they are indexed, searchable, and ultimately reusable using the Microsoft Office 2003 Research and Reference task pane.

John R. Durant

MSDN Magazine February 2005

...

Read more!

Popular Articles

C# 2.0 introduces a wealth of exiting new features, such as generics, iterators, partial classes and anonymous methods. While generics are the most talked-about feature especially for former classic C++ developers, the rest of the new features are important additions to your .NET development arsenal, enhancing power and improving overall productivity. This article is dedicated to all the new C# 2.0 capabilities besides generics to give you a good overall picture of the upcoming features.

Juval Lowy

MSDN ...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

Kenny Kerr

MSDN Magazine May 2008

...

Read more!

When incorporating the ASP.NET DataGrid control into your Web apps, common operations such as paging, sorting, editing, and deleting data require more effort than you might like to expend. But all that is about to change. The GridView control--the successor to the DataGrid-- extends the DataGrid's functionality it in a number of ways. First, it fully supports data source components and can automatically handle data operations, such as paging, sorting, and editing, as long as its bound data source object supports these capabilities. In addition, ...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

Our Blog

Silverlight and SharePoint provide a simple, yet powerful, infrastructure for building intranet and extranet applications with sophisticated user interface designs and interactions.

In the November 2008 issue of MSDN Magazine, Steve Fox and Paul Stubbs demonstrate how to build a SharePoint Web Part as a wrapper for a Silverlight application.

...

Read more!

Earlier this year MSDN Magazine embarked on a collaborative project with Behind the Code, an interview program airing on MSDN Channel 9. In this program, Robert Hess interviews prominent developers at Microsoft, and those developers also write a column for { End Bracket } in MSDN Magazine. In the newest interview, Richard Ward talks about working on the core infrastructure components of future versions of Windows, as well as ...

Read more!

Windows Workflow Foundation (WF) imposes some restrictions on the developer authoring programs that target it. But in return WF offers a powerful, flexible, and extensible set of runtime services such as support for long-running code.

In the December 2008 issue of MSDN Magazine, Josh Lane provides some best practices to consider ...

Read more!

Windows Presentation Foundation (WPF) adds functionality to the Microsoft .NET Framework so that you actually can reliably keep bound controls synchronized with their data sources.

In the December 2008 issue of MSDN Magazine, Ken Getz demonstrates how to use the ObservableCollection class provided by WPF to keep bound controls in ...

Read more!

With the releases of LINQ to SQL and the ADO.NET Entity Framework, developers now have two products from Microsoft designed to tie together relational data and object-oriented programming.

In the December 2008 issue of MSDN Magazine, Anthony Sneed provides a roadmap to these technologies and demonstrates how you can create ...

Read more!

Outlook Add-Ins
Improve Your Outlook With Visual Studio Tools For Office
John R. Durant

This article discusses:
  • Background scenario for an Outlook add-in
  • A high-level look at the add-ins architecture
  • Processing e-mail attachments
  • Generating customer correspondence
This article uses the following technologies:
Visual Studio 2005 Tools for the Microsoft Office System
Code download available at: OutlookAddIns.exe (158 KB)
Browse the Code Online
Microsoft Visual Studio Tools for Office 2003, while useful to many developers who wrote tools for Office, supported only Microsoft® Word and Excel®. The new Visual Studio® 2005 Tools for the Microsoft Office System goes farther, adding tools that help you create managed code add-ins for Outlook® 2003.
You can create managed add-ins for Outlook using the Shared Add-in template in Visual Studio 2005 Standard Edition, but you'll miss out on the improvements that have been made in Visual Studio 2005 Tools for the Microsoft Office System that make the process easier and faster.
This article walks you through a sample add-in application for Outlook 2003. Along the way I'll show off some of the things that are possible when creating an Outlook add-in solution using this new version (Visual Studio Tools for Office). The add-in helps a company bring its inventory management data into a more meaningful and immediate sales promotion context.

The Scenario
The business scenario for the sample deserves a little bit of background to help you understand the overall application as well as the technical details. World Wide Importers sells products that other companies and institutions purchase as part of their own marketing campaigns. Items sold include branded key chains, pens, paper, letter openers, and other such office supplies.
In the past, World Wide Importers sales agents have received reports containing data about previous customer orders, but they had to sift through this data manually to find out what was meaningful, slowing the sales process, and causing some missed opportunities. Further, the sales agents felt they spent too much time creating letters and other material that suggested products to their customers. They wanted these materials to be both customer-specific and easy to compile.
My sample application, the Outlook Sales Order Prompt add-in, solved the problem. As seasonal item inventory comes in, the inventory manager runs a batch job from the database. The batch job in turn produces an Excel spreadsheet for each customer and sends the spreadsheet to the customer's sales agent as an attachment. These spreadsheets are not produced in the Excel binary format but rather in the XML spreadsheet format, SpreadsheetML.
The sales agent can review the data in the spreadsheet and, by clicking a button, create a promotional letter containing customer-specific recommended items from the spreadsheet and contact data from Outlook. A resulting promotional letter is saved as a Word document using the Word XML file format, WordProcessingML. An assistant can print these letters and send them to the customers. The entire process, up to the point of sending the physical letter, takes a fraction of a second.

Add-In Architecture
As you can see from the application's data flow, shown in Figure 1, the add-in handles transforming the Excel spreadsheet into two different XML documents. The first is a raw data document, and the second is a fully formatted Word document. While the transformation from spreadsheet to Word document can be done in one operation, there are benefits to having an interim XML document with no Word formatting. For example, what if another system or process needed access to the raw XML data? What if additional data elements were needed in the raw XML data file? It is easier to manage the transformations and various markup demands by isolating the operations.
Figure 1 Data Flow of Outlook Sales Order Prompt Add-In 
Fortunately, users need not worry about any of these details as they interact with the add-in. Its simple menu contains three items, as shown in Figure 2. These menu items simply execute various data transformations supported by the add-in.
Figure 2 Add-In Menu Items 
One key advantage of using Visual Studio Tools for Office to create an Outlook add-in is that the development experience has been greatly simplified. Creating add-ins using the old IDTExtensibility2 model meant that you had five different events to choose from, causing confusion about which event to use for different programming tasks. Now there are just two events, ThisApplication_Startup and ThisApplication_Shutdown. These events are contained in a ThisApplication file that the tools create automatically. In the case of the sample add-in, which was written in Visual Basic, the file is called ThisApplication.vb. The principles discussed here apply to add-ins created in C# as well.
The SalesOrderPrompt add-in runs code at startup to add its menu to the menu bar in Outlook (see Figure 3). The add-in also acquires a reference to a specific Outlook folder because it will need to work with this reference later on. Finally, the startup code gains a pointer to the active Explorer object so that the add-in can disable the menu if the user doesn't have the target folder selected. The only other work that precedes the ThisApplication_Startup method is the class-level variable declarations for the various folders, Explorers, buttons, strings, and other items used during the add-in's lifetime.
Private WithEvents _Explorers As Outlook.Explorers
Private WithEvents _Explorer As Outlook.Explorer
Private _taskFolder As Outlook.Explorer
Private _SOPFolder As Outlook.MAPIFolder
Private _topMenu As Office.CommandBarPopup
Private _results As Outlook.Results
Private _searchTerm As String
Private _contactEntryID As String
Private _attachmentPath As String
Private WithEvents _createLetter1 As Office.CommandBarButton
Private WithEvents _createLetter2 As Office.CommandBarButton
Private WithEvents _createLetter3 As Office.CommandBarButton
Private _menuBar As Office.CommandBar
Dim WithEvents _mailTarget As Outlook.MailItem

Private search As Outlook.Search = Nothing

Private Sub ThisApplication_Startup( _
        ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Startup
    BuildTopMenu()
    _SOPFolder = OpenMAPIFolder()
    _Explorer = Me.ActiveExplorer()
End Sub

Function OpenMAPIFolder() _
        As Microsoft.Office.Interop.Outlook.MAPIFolder
    Dim SOPFolder As Microsoft.Office.Interop.Outlook.MAPIFolder
    Dim f As Microsoft.Office.Interop.Outlook.MAPIFolder = Nothing
    SOPFolder = Me.ActiveExplorer.Session.GetDefaultFolder( _
        Outlook.OlDefaultFolders.olFolderInbox)
    For Each f In SOPFolder.Folders
        If f.Name = "SalesOrderPrompt" Then Exit For
    Next
    Return f
End Function
Building a menu in Outlook is easy. The add-in has a separate procedure to build the menu, and it does so by referencing the ActiveMenuBar property of the Outlook ActiveExplorer (see Figure 4). Each Explorer object in Outlook has a CommandBars collection that you can access to add, remove, or modify its collection of menu bars and their controls. The Sales Order Prompt add-in appends a new menu bar to the Outlook standard menu bar and then adds three new buttons to the menu.
Private Sub BuildTopMenu()
    _menuBar = Me.ActiveExplorer().CommandBars.ActiveMenuBar

    _topMenu = CType(_menuBar.Controls.Add( _
        Office.MsoControlType.msoControlPopup, , True), _
        Office.CommandBarPopup)
    _topMenu.Caption = "SOP Add-in"
    _topMenu.Visible = True

    _createLetter1 = CType(_topMenu.Controls.Add( _
        Office.MsoControlType.msoControlButton, , , True), _
        Office.CommandBarButton)
    _createLetter1.Caption = "Create Basic Letter"
    _createLetter1.Visible = True
    _createLetter1.Enabled = False

    _createLetter2 = CType(_topMenu.Controls.Add( _
        Office.MsoControlType.msoControlButton, , , True), _ 
        Office.CommandBarButton)
    _createLetter2.Caption = "Create Personal Letter"
    _createLetter2.Visible = True
    _createLetter2.Enabled = False

    _createLetter3 = CType(_topMenu.Controls.Add( _
        Office.MsoControlType.msoControlButton, , , True), _
        Office.CommandBarButton)
    _createLetter3.Caption = "Create Premiere Letter"
    _createLetter3.Visible = True
    _createLetter3.Enabled = False
    _topMenu.Enabled = False
End Sub
Looking at Figure 4 closely you can see more of the advantages that Visual Studio Tools for Office offers. For example, the add-in provides easy access to the Outlook object model objects by setting up the reference to the primary Outlook application instance. This reference is an instance of the Application object in the Outlook object model. You access this object instance, ThisApplication, using the Me keyword in Visual Basic or the this keyword in C#.
Note that the Application object instance accessed through ThisApplication does not derive from Microsoft.Office.Interop.Outlook. Rather, it derives from Microsoft.Office.Tools.Outlook.Application. This is a custom namespace that accompanies Visual Studio Tools for Office. The key advantage in deriving from this class rather than the normal Primary Interop Assembly (PIA) class is that it allows your add-in code to be forward-compatible by decoupling it from the regular PIA namespace. If future versions of Office change the PIA namespace, your code can remain stable as far as the primary object reference is concerned.

Attachment Processing
The add-in's primary operation runs when the user clicks one of the buttons. The custom add-in menu is only enabled if the user has selected a custom folder associated with the Sales Order Prompt add-in (see Figure 5) and only one e-mail item is selected in the special folder.
Figure 5 Target Outlook Folder 
Recall that the overall scenario specified that the company's database sends an e-mail containing a report for each customer. The customer's sales agent receives the e-mail, reviews the report, and fires off the main code of the add-in by clicking the custom menu button. Restricting the selection of only one e-mail item in the custom folder reduces the code complexity in the add-in. The code to enforce both of these key constraints is in the FolderSwitch and SelectionChange events (see Figure 6) of the Explorer object that the add-in references at startup.
Private Sub _Explorer_FolderSwitch() Handles _Explorer.FolderSwitch
    _topMenu.Enabled = _
        (_Explorer.CurrentFolder.EntryID = _SOPFolder.EntryID)
End Sub

Private Sub _Explorer_SelectionChange() _
    Handles _Explorer.SelectionChange

    If _Explorer.CurrentFolder.EntryID = _SOPFolder.EntryID _
            And _Explorer.Selection.Count = 1 Then
        _mailTarget = _Explorer.Selection(1)
        _createLetter1.Enabled = True
        _createLetter2.Enabled = True
        _createLetter3.Enabled = True
    Else
        _createLetter1.Enabled = False
        _createLetter2.Enabled = False
        _createLetter3.Enabled = False
    End If
End Sub
With these conditions met, the user clicks the button to process the e-mail attachment in order to create a promotional letter. The attachment processing starts by gaining a reference to the selected e-mail's Excel spreadsheet attachment. It finds the name of the customer in the spreadsheet, searches among the user's Outlook contacts to find the contact information for the customer, and merges the contact information with selected data from the Excel spreadsheet report in a raw data XML file. The code converts the raw data XML file into a fully formatted Word document saved in a centrally accessible location. These steps begin in the click event for a button on the add-ins custom menu (see Figure 7).
Private Sub _createLetter1_Click( _
    ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, _
    ByRef CancelDefault As Boolean) Handles _createLetter1.Click

    Try
        If _mailTarget.Attachments.Count = 0 Then
           MessageBox.Show("E-mail message has no attachment to process")
           Exit Sub
        End If

        Dim attachment As Outlook.Attachment
        attachment = _mailTarget.Attachments.Item(1)

        Dim excelExport As New XmlDocument()
        _attachmentPath = Path.GetTempPath() & attachment.FileName
        attachment.SaveAsFile(_attachmentPath)
        excelExport.Load(_attachmentPath)

        Dim namespaceManager As System.Xml.XmlNamespaceManager
        namespaceManager = New _
           System.Xml.XmlNamespaceManager(excelExport.NameTable)
        namespaceManager.AddNamespace("ss", _
           "urn:schemas-microsoft-com:office:spreadsheet")

        _searchTerm = excelExport. _
           SelectSingleNode("/ss:Workbook/ss:Worksheet/" _
           & "ss:Table/ss:Row/ss:Cell/ss:NamedCell[@ss:Name=" _
           & "'Customer']/../ss:Data/node()", namespaceManager).Value
        UseAdvancedSearch()

    Catch exc As System.Exception
        MessageBox.Show(exc.Message)
    End Try
End Sub
No system is foolproof, so the add-in code checks to see whether the selected e-mail has no attachment and lets the user know if this is the case. With one attachment found, the add-in saves the attachment to a local temporary directory so that its contents can be loaded into an XmlDocument instance. A quick search using an XPath statement lets the add-in find the name of the customer so that it can find contact information.
Saving the file to a local temporary directory is necessary because Outlook exposes the attachment as a binary object with no clearly discernible type information. A potential improvement of the add-in could include the conversion of the attachment's binary stream to its simple text-based XML format.
In order to find the customer's contact information, the add-in leverages the AdvancedSearch object in Outlook, as shown in Figure 8. Using this object, the add-in can look for all contacts whose CompanyName property matches the search term extracted from the spreadsheet attachment.
Private Sub UseAdvancedSearch()
    Dim contacts As Outlook.MAPIFolder = _
        Me.ActiveExplorer().Session.GetDefaultFolder( _
        Outlook.OlDefaultFolders.olFolderContacts)

    Try
        Dim filter As String = String.Format("{0} LIKE '%{1}%'", _
            "urn:schemas:contacts:o", _searchTerm)
        Dim scope As String = String.Format("SCOPE (" _
            & "'shallow traversal of ""{0}""')", _
            contacts.FullFolderPath)
        search = Me.AdvancedSearch(scope, filter, True, _searchTerm)

    Catch ex As System.Exception
        MessageBox.Show("There was an error looking for a customer:" _
            & _searchTerm, "Search")

    End Try
End Sub
The AdvancedSearch object in Outlook works asynchronously so that while the search is underway, users can return to other tasks and not have to wait for the search to complete. Because AdvancedSearch works asynchronously, there is no consistent linear progression from when a user clicks a button on the menu and when the search completes. It is possible for a user to launch the search, and then launch a second search that completes before the first search completes. Thus, the add-in needs a way to distinguish the results of one search from another.
One way to do this is shown in Figure 8 where the search term variable, _searchTerm, is passed as an argument to the AdvancedSearch constructor. This sets the Tag property of the AdvancedSearch object equal to the value of the search term. Then, as the search completes, the add-in can look at this property to determine which search the results belong to. When the search finishes, Outlook fires the AdvancedSearchComplete event and passes as an argument a Search object containing the results of the search (see Figure 9).
Private Sub ThisApplication_AdvancedSearchComplete( _
    ByVal SearchObject As Microsoft.Office.Interop.Outlook.Search) _
    Handles Me.AdvancedSearchComplete

    Dim contactResultsForm As ContactResultsForm
    Dim contactResultArray() As ContactResult = Nothing

    If SearchObject.Tag = _searchTerm Then
        _results = SearchObject.Results

        If _results.Count > 0 Then
            Dim i As Integer
            Dim _item As Outlook.ContactItem

            For i = 1 To _results.Count
                If _results.Item(i).Class = _
                    Outlook.OlObjectClass.olContact Then
                    _item = _results.Item(i)
                    ReDim Preserve contactResultArray(i - 1)
                    contactResultArray(i - 1) = New _
                        ContactResult(_item.FullName, _
                        _item.CompanyName, _item.EntryID)
                End If
            Next

            If contactResultArray.Length > 1 Then
                contactResultsForm = New _
                    ContactResultsForm(contactResultArray)
                contactResultsForm.ShowDialog()
                _contactEntryID = contactResultsForm.EntryID
                ProcessXmlAttachment()
            Else
                _contactEntryID = contactResultArray(0).EntryID
                ProcessXmlAttachment()
            End If
        End If
    End If
End Sub

Creating the Letter
In creating the actual letter, the add-in does its primary job of merging data, transforming XML, and producing Word documents to be sent to the customer. The add-in checks to see whether the search results are for the Sales Order Prompt search by looking at the Tag property and seeing if it matches the value of the _searchTerm variable that was set when the search began. As the add-in loops through the results, it verifies that each result's olObjectClass property is set to the contact olContact. The add-in does not process non-contact items returned from the search.
For each contact item found, the add-in creates an instance of a custom ContactResult class and places it into an array. If there is only one contact for a customer, then the add-in can proceed directly with merging the contact data with the spreadsheet data and doing the transformations. If more than one contact exists for a customer, the add-in displays a form that lets the user select one of the contacts.
Once the add-in knows which contact's data to include, it sets a class-level variable, _entryID, to the entryID property of the target contact. This allows the add-in to load the contact from another procedure, ProcessXmlAttachment (see the code download for details).
At an earlier point, the add-in saved the spreadsheet in a temporary directory. Now the add-in loads that XML file into a new XmlDocument instance. It then loads an XSL stylesheet from disk, the location of which is specified in an application-level setting named ExcelExport. With the source document and transformation document specified, the add-in transforms the source document and loads the resulting XML document into memory using the StringWriter object instance.
Next, the add-in merges the data about the sales agent and the company's contact into the new in-memory XML document. This means adding new nodes and appending them to the XML document. To reduce the clutter in this part of the code, the add-in repeatedly calls the AddXmlElement function to add the new node elements:
Private Function AddXmlElement(ByRef ParentDoc As XmlDocument, _
      ByVal ElementName As String, ByVal InnerText As String) As XmlNode
    Dim newNode As XmlNode
    newNode = ParentDoc.CreateElement(ElementName)
    newNode.InnerText = InnerText
    Return newNode
End Function
The add-in loads a special XML file in a directory located in the user's special settings: C:\Documents and Settings\[USERNAME]\Local Settings\Application Data\SOP. This directory contains a file called CurrentUser.XML, which contains information about the logged-on sales agent. Data from this file and from the contact returned by the Outlook search is added to the in-memory XML document.
Finally, the add-in loads another XSL stylesheet using a different application-level variable, ProposalLetter. This transformation converts the data-only XML into a fully formatted Word document.
The add-in produces the XML document using an instance of the XmlTextWriter object. It saves the final document in a special directory whose location it keeps in another application-level variable, DocStore. When it saves the file, it includes the name of the company and the current date in the document's name.
Finally, I don't think anyone likes to have menus cluttering up the user interface when they are not really needed. So the add-in responsibly removes the custom menu from the Outlook standard menu bar when the add-in is unloaded. Unloading the add-in can occur when the user quits Outlook or when the user disables it in the list of COM add-ins. In either case, the add-in's ThisApplication_Shutdown event fires:
Private Sub ThisApplication_Shutdown( _
        ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Shutdown
    _topMenu.Delete()
End Sub
Another advantage of using Visual Studio Tools for Office to create your Outlook add-ins is that the tools create a setup project for you at project creation. Thus, your solution will contain two projects, the first being your add-in itself, and the second being the setup project for the add-in.
Now you can add special launch conditions and craft your setup package to suit your needs. But the tools have already configured the basic properties for you. For example, the output of your add-in has already been referenced along with dependencies on the Office PIA and the Microsoft .NET Framework 2.0, among others.

Conclusion
Many companies want to make Outlook and the data that it handles a more integral part of their core business processes. Writing solutions in managed code is one obvious way to achieve this. Until now, creating, debugging, and maintaining managed code add-ins for Outlook has been a sizable challenge. Debugging could be a frustrating experience, and there was too wide a gap between the COM world of Office and the managed world of the Microsoft .NET Framework.
Visual Studio Tools for Office narrows that gap considerably and delivers a dependable design time and run-time environment for managed code Outlook add-ins. Rather than spending your time troubleshooting development tools, you can now get right into the core programming tasks and create powerful Outlook add-ins more easily. For more information on developing add-ins for Outlook, see Visual Studio Tools for Office Technical Articles.

John R. Durant is a Program Manager for Visual Studio Tools for Office and is a frequent author and speaker on a wide variety of development topics (blogs.msdn.com/johnrdurant). He coauthored the latest edition of the XML Programming Bible (Wiley & Sons, 2003) and is authoring a new book on Office 12 programmability.

Page view tracker