What's New in Microsoft Office Outlook 2003 for Developers?
Micro Eye, Inc.
Microsoft Outlook MVP
Microsoft® Visual Basic® .NET 2003
Microsoft Office Outlook® 2003
Microsoft Windows® SharePoint™ Services
Microsoft Office SharePoint Portal 2003
Summary: Learn how to use Visual Basic .NET to integrate Microsoft Office Outlook 2003 with Microsoft SharePoint™ Products and Technologies. Although changes to the Outlook Object Model are small in scope, new properties are added to support integration with Microsoft Windows SharePoint Services (formerly known as SharePoint Team Services) and Microsoft Office SharePoint Portal Server 2003. The sample Visual Basic .NET Outlook Add-in allows you to import a Contact list from a SharePoint site into Outlook and directly export Outlook Contact items to a SharePoint site. (32 printed pages)
Note The information in this article that applies to Microsoft Windows SharePoint Services also applies to Microsoft Office SharePoint Portal Server 2003, which is built on the Windows SharePoint Services platform. The code samples can be used on sites created with SharePoint Portal Server.
|Download the odc_OLWhatsNew2k3Sample sample file. (599 KB)|
Microsoft® Office Outlook® 2003 offers many new and improved features in terms of performance, connectivity, and usability. The programming enhancements to Outlook are small in scope, with the exception of a major change in Outlook object model security. Previous versions of Outlook blocked many properties and methods to prevent the spread of e-mail worms and viruses. Outlook 2003 trusts all installed Add-ins including Microsoft Outlook Visual Basic® for Applications (VBA) by default in terms of access to previously blocked properties and methods. Outlook developers have an opportunity to create new integration scenarios with Microsoft Windows® SharePoint™ Services. Windows SharePoint Services provides the foundation for Microsoft Office SharePoint Portal Server 2003. Microsoft SharePoint Products and Technologies offer a new Web-based collaboration platform for rapid deployment of team-based Web sites and sharing contacts, tasks, documents, and events among team members. For more information about Microsoft SharePoint Products and Technologies, see the Microsoft SharePoint Products and Technologies home page on MSDN.
The changes to the Outlook object model reflect the changes to the Outlook user interface. A small number of these changes relate to integration with SharePoint Products and Technologies. The following tables provide a quick overview of the new objects, methods, properties, and events in the Microsoft Office Outlook object model followed by sample VBA code for some selected events and methods.
Table 1. New objects in the Outlook object model
|Conflicts||Any Outlook item-type object such as a MailItem object||The Conflicts object contains a set of Conflict objects representing all items that are in conflict with a particular Outlook item such as a MailItem, AppointmentItem, TaskItem, and so forth. If the Count property of this object is non-zero, the item is involved in a conflict.|
|Conflict||Conflicts collection object||Represents an item that is in conflict with another Outlook item.|
Table 2. New methods in the Outlook object model
|AddPicture||ContactItem object||Adds a picture to an Outlook Contact. You must supply the full path to the picture in the Picture argument. If the Contact already has a picture attached to it, this method overwrites the existing picture.|
|AddStoreEx||Namespace object||Adds a Personal Folders file (PST) in the specified format to the current profile. Specify the file location of the PST by using the Store argument. Specify the format of the PST with the Type argument. Valid Type arguments are olStoreANSI, olStoreDefault, or olStoreUnicode. Use olStoreANSI to preserve compatibility with Outlook 97-2002 PST formats.|
|DeselectFolder||Explorer object||If the Outlook Explorer is currently displaying the Calendar folder specified in the MAPIFolder argument simultaneously with another Calendar folder, the specified folder is closed. Applies to Calendar folders only.|
|IsFolderSelected||Explorer object||Returns a Boolean value that determines if the folder specified in the MAPIFolder argument is currently displayed in the Outlook Explorer window.|
|RemovePicture||ContactItem object||Removes the picture associated with an Outlook Contact.|
|Save||Search object||Saves the Search object using the name specified in the Name argument and returns a MAPIFolder object representing the Search Folder created by the Search object. Create complex search conditions using search syntax explained in the Microsoft Exchange Server 2003 Software Development Kit.|
|SelectFolder||Explorer object||Displays the folder specified in the MAPIFolder argument if it can be displayed simultaneously with the current folder in the Outlook Explorer window. If the folder cannot be displayed in a side-by-side view (Calendar folders only), the Explorer CurrentFolder object changes to the specified folder.|
|SetControlItemProperty||Inspector object||Binds an Outlook object model property specified in the PropertyName argument to a control on an inspector specified in the Control argument|
The following code example creates a Search object that returns all Inbox items with "RE:" in the message subject. Using the new Save method of the Search object, it creates a Search Folder named RE Search and prints the number of items in the Search Folder to the Immediate window. Search Folders are one of the most powerful new features of Outlook. Search Folders allow users to see consolidated search results for items located in multiple Outlook folders based upon criteria such as Large Messages, For Follow Up, or Unread Mail. Search Folders appear under the Search Folders node in the Outlook folder tree as illustrated in Figure 1.
Note A user cannot edit the Criteria for Search Folders that are created programmatically. The Criteria button is disabled in the Custom Search Folder dialog box.
Figure 1. The Search Folders node in Outlook
Sub CreateSearchFolder() Dim olApp As Outlook.Application Dim objSearch As Outlook.Search Dim folInbox As Outlook.MAPIFolder Dim strFolderPath As String Dim strScope As String Dim strFilter As String Dim objSearchFolder As Outlook.MAPIFolder On Error Resume Next Set olApp = New Outlook.Application 'Create a MAPIFolder object for Inbox Set folInbox = olApp.GetNamespace("MAPI") _ .GetDefaultFolder(olFolderInbox) 'Get the Folder Path strFolderPath = folInbox.FolderPath 'Build a scope string strScope = "SCOPE ('shallow traversal of " _ & AddQuotes(strFolderPath) & "')" 'Build a filter string (WHERE clause without the WHERE) strFilter = AddQuotes("urn:schemas:mailheader:subject") _ & " LIKE 'RE:%'" 'Create the Search object by calling AdvancedFind Set objSearch = _ olApp.AdvancedSearch(strScope, strFilter, False, "RE Search") 'Save the Search as a Search Folder Set objSearchFolder = objSearch.Save ("RE Search") Debug.Print objSearchFolder.Items.Count End Sub Public Function AddQuotes(strText) As String On Error Resume Next AddQuotes = Chr$(34) & strText & Chr$(34) End Function
One of the problems frequently encountered in Outlook development is creating a custom form page and control programmatically and binding that control to an Outlook item property. The SetControlItemProperty is a new method of the Outlook object model that lets you set a control binding programmatically. The following code example creates a meeting request item, adds a page named Binding Example and then adds a Text Box control to the Binding Example page. When a user clicks the To. . . button to set attendees for the meeting request, those attendees appear in the text box on the Binding Example page of the Appointment form.
Sub SetControlItemPropertyExample() Dim myInspector As Outlook.Inspector Dim myAppt As Outlook.AppointmentItem Dim ctrl As Object Dim ctrls As Object Dim myPages As Outlook.Pages Dim myPage As Object Set myAppt = Application.CreateItem(olAppointmentItem) Set myInspector = myAppt.GetInspector myAppt.MeetingStatus = olMeeting myAppt.Subject = "Test Appointment" Set myPages = myInspector.ModifiedFormPages Set myPage = myPages.Add("Binding Example") Set ctrls = myPage.Controls Set ctrl = ctrls.Add("Forms.TextBox.1") ctrl.Top = 10 ctrl.Left = 10 myInspector.SetControlItemProperty ctrl, "To" myAppt.Display End Sub
Table 3. New properties in the Outlook object model
|AutoResolvedWinner||Any Outlook item-type object such as a MailItem object||Returns a read-only Boolean value that determines if the item is a winner of an automatic conflict resolution.|
|Conflicts||Any Outlook item-type object such as a MailItem object||The Conflicts object contains a set of Conflict objects representing all items that are in conflict with a particular Outlook item such as a MailItem or AppointmentItem. If the Count property of this object is non-zero, the item is involved in a conflict.|
|EnableSharedAttachments||MailItem object||Sets or returns a Boolean value that determines whether the Attachment Options task pane is displayed in the Outlook Inspector for a message. Using the Attachment Options task pane, a user can collaborate using a Document Workspace site instead of exchanging multiple versions of the attachment in e-mail messages.|
|ExchangeConnectionMode||Namespace object||Returns an OlExchangeConnectionMode constant that indicates the current connection mode the user is using. Returns olNoExchange in a POP, IMAP, or Hotmail profile.|
|FlagIcon||MailItem or MeetingItem object||Returns or sets an olFlagIcon constant indicating one or none of the six flag types supported in Outlook 2003.|
|HasCoverSheet||MailItem object||Returns or sets a Boolean value that determines the setting of the Use Cover Sheet option in the Fax user interface, which indicates what is displayed in the body of the mail item.|
|HasPicture||ContactItem object||Returns a read-only Boolean value to determine if a ContactItem object has a picture associated with it.|
|IsIPFax||MailItem object||Sets or returns a Boolean value that determines if an e-mail item is a fax.|
|IsSharePointFolder||MAPIFolder object||Returns a read-only Boolean value that determines if the folder is a folder associated with a SharePoint site.|
|MeetingWorkspaceURL||AppointmentItem or MeetingItem object||Returns the URL for the Meeting Workspace site to which the meeting or appointment item is linked.|
|Permission||MailItem object||Returns or sets an olPermission constant that determines whether a message has protected contents using Microsoft® Windows® Rights Management (RM) technology.|
|PermissionService||MailItem object||Sets or returns an OlPermissionService constant that determines the permission service that is used when sending an e-mail message protected with Windows Rights Management technology.|
|SenderEmailAddress||MailItem, MeetingItem, or PostItem object||Returns a read-only String that represents the e-mail address of the sender of the e-mail message, meeting item, or post.|
|SenderEmailType||MailItem, MeetingItem, or PostItem object||Returns a read-only String that represents the type of entry for the e-mail address of the sender of the message, meeting item, or post, such as 'SMTP' for an Internet address or 'EX' for a Microsoft Exchange server address.|
|ShowItemCount||MAPIFolder object||Sets or returns an OlShowItemCount constant that indicates whether to display the number of unread messages in the folder or the total number of items in the folder in the navigation pane.|
Notice that properties such as EnableSharedAttachments, IsSharePointFolder, and MeetingWorkspaceURL relate to Outlook integration with SharePoint Products and Technologies. However, all of these properties only reflect user actions in the Outlook user interface. They do not let you directly create a Meeting Workspace site, Document Workspace site, or linked Contact items. To accomplish some of these actions programmatically, see the "Integrating with SharePoint Sites" section later in this article.
Table 4. New events in the Outlook object model
|NewMailEx||Event for Application object||This event returns a comma-separated string containing the Entry IDs of all new items received in the Inbox. It fires for all items received in the Inbox, including standard messages, meeting requests, and task requests. If you are running in cached Exchange mode, it fires before any client-based rules that might move Inbox items to other mailbox folders. This event fires before the NewMail event.|
The NewMailEx event is a welcome addition to the Outlook object model. It provides complete information about new mail items in comparison to the NewMail event, which only fires when New Mail arrives but does not inform you about the specific items that have arrived. The following code fragment shows you how to use the NewMailEx event in Outlook VBA. It prints the message subject to the Immediate window.
Private Sub Application_NewMailEx(ByVal EntryIDCollection As String) Dim varEntryIDs Dim objItem Dim i As Integer On Error Resume Next varEntryIDs = Split(EntryIDCollection, ",") For i = 0 To UBound(varEntryIDs) Set objItem = Application.Session.GetItemFromID(varEntryIDs(i)) Debug.Print "NewMailEx " & objItem.Subject Next End Sub
One of the most significant developments for Outlook 2003 developers is a revision of object model security, most commonly known as the Outlook object model guard component of the Outlook E-mail Security Update. The Outlook E-mail Security Update blocks certain properties, methods, and objects in the object model to prevent e-mail worms and viruses from using the object model for malicious purposes. Although some properties are added to the list of blocked properties, in general the security model is improved for Outlook 2003 developers because all installed COM Add-ins (including Outlook VBA code which is in itself an installed Add-in) and VBScript code behind published forms is now trusted from the perspective of the Outlook E-mail Security Update. Your calls to blocked object model properties and methods no longer display the security warning dialog box shown in Figure 2.
Figure 2. The Security Warning dialog box
A Practical Security Example
In order for your calls to blocked object model properties and methods to work without the display of the security warning dialog box shown in Figure 2, you must use the secure Outlook Application object returned in the OnConnection event of a COM Add-in, or the Application object in Outlook VBA or in code behind Outlook forms.
Important If you use the New keyword to instantiate another instance of the Outlook Application object, all objects that derive from that Application object and their blocked properties and methods cause the Outlook security warning dialog boxes to appear. Therefore, you may need to update some add-ins.
The VBA code in UnTrustedCode displays the security warning dialog box while the code in TrustedCode uses the trusted Application object and does not display security warnings.
Sub UnTrustedCode() Dim olApp As New Outlook.Application Dim oMail As Outlook.MailItem Set oMail = _ olApp.Session.GetDefaultFolder(olFolderInbox).Items(1) MsgBox oMail.SenderEmailAddress, vbInformation End Sub Sub TrustedCode() Dim olApp As Outlook.Application Set olApp = Application Dim oMail As Outlook.MailItem Set oMail = _ olApp.Session.GetDefaultFolder(olFolderInbox).Items(1) MsgBox oMail.SenderEmailAddress, vbInformation End Sub
Blocked Properties and Methods
The following table lists the properties and methods that display the Object Model Guard Warning dialog boxes in Outlook 2003. New blocked properties for Outlook 2003 are marked with an asterisk (*).Outlook 2003 adds protection to the body properties of all items including the IMaddress field of contact items, and the wordeditor and htmleditor inspectors.
Table 5. Blocked properties and methods
|Object||Restricted Properties||Restricted Methods|
|AddressEntries||Any property||Any method|
|AddressEntry||Any property||Any method|
|ItemProperties||Any restricted property for an Item|
|Recipient||Any property||Any method|
|Recipients||Any property||Any method|
Outlook Security in the Context of Microsoft Exchange Server
The revised security model discussed previously applies to Outlook using default security settings. Exchange Administrators can still use the Outlook Security Settings public folder and the Administrative form to create customized security settings for object model calls and Trusted Add-ins.
If you use the Administrative form to establish new default security settings and exception groups for your organization, these settings override the default security settings for Outlook. In summary, Administrators must decide whether to:
- Use the list of trusted Add-ins in the security form
- Trust all installed Add-ins
- Trust no Add-ins
These settings are controlled by using the AddinTrust registry key. When an Add-in is trusted, calls to blocked methods and properties will not display the Outlook security dialogs. The AddinTrust key is located under Software\Policies\Microsoft\Office\11.0\Outlook\Security. Administrators can use the Custom Installation Wizard to rollout this key for new Office installations or the Custom Maintenance Wizard to add the key to existing installations. Both the Custom Installation Wizard and Custom Maintenance Wizard are available as components of the Office 2003 Editions Resource Kit.
0- Look to CheckAdminSettings key for COM Add-in trust setting. The CheckAdminSettings key determines if Outlook uses the Administrative Form to determine if an add-in is in the list of trusted add-ins. If the CheckAdminSettings key is present under Software\Policies\Microsoft\Office\11.0\Outlook\Security, only trust the add-ins in the trusted add-ins list on the Administrative Form. If CheckAdminSettings key is not present, then Trust all installed COM Add-ins.
1- Trust all installed COM Add-ins
2- Trust no installed COM Add-ins
The benefit to the second setting (1) is that administrators can use the security form for all other settings without having to explicitly trust each Add-in on the security form.
For more information about Outlook security settings for Exchange Administrators, see Customizing Outlook 2003 to Help Prevent Viruses.
Additional Security Changes for COM Add-ins
For administrators who want an extra level of security regarding trusted code privileges, Outlook observes some new security settings introduced in Microsoft Office System. Previously, Office applications used Low, Medium, and High security levels to control code execution on an application-by-application basis for Office applications. Office 2003 still observes application-by-application security levels for code execution. However, the Microsoft Office System introduces a new setting for Very High security. The Very High security option is shown on the Security Level page of the Macro Security dialog box shown Figure 3.
Figure 3. Setting the macro security level
The Macro Security option operates in conjunction with the Trusted Publishers page on the Security dialog box.
Figure 4. Trusting all installed add-ins and templates
Unlike previous versions of Outlook, Outlook 2003 exposes a check box that allows you to control whether or not installed Add-ins are trusted or not. The default macro security level is High and the default is to trust all installed add-ins and templates. Administrators can control the default security levels with the Office System Custom Installation Wizard. For more information, see Methods of Customizing Office. If you clear the Trust all installed add-ins and templates check box and the macro security setting is set to the default High Security level, then only digitally signed Add-ins are trusted to load. The actual load behavior of the Add-in depends upon the LoadBehavior setting in the Windows Registry. Most Add-ins are set to load on Outlook startup. The Add-in must be signed with a Class 3 code-signing certificate. For more information about Class 3 code-signing certificates, see Code Signing Digital IDs. The following table shows how VBA and Add-in code load based upon the security level and whether the Trust all installed add-ins and templates box is checked.
Table 6. Security settings and default behavior
|Security Level||Trust all installed add-ins and templates (checked)||Trust all installed add-ins and templates (unchecked)|
|Very High||Checked by default so all Add-ins load, VBA code does not load||Not available under Very High security level|
|High||All Add-ins load, VBA code does not load||Only digitally signed (Class 3 Certificate) and trusted Add-ins load, unsigned Add-ins do not load, VBA code does not load|
|Medium||All Add-ins load, VBA code loads if user clicks Enable Macros in security warning dialog box||Only digitally signed (Class 3 Certificate) and trusted Add-ins load, unsigned Add-ins load if the user selects Enable Macros for each individual Add-in (including Outlook VBA which is actually an Add-in registered under HKLM)|
|Low||All Add-ins load, VBA code loads||Digitally signed (Class 3 Certificate) and unsigned Add-ins load, VBA code loads|
You might be wondering after the previous discussion whether Outlook 2003 offers an expanded window of development opportunities. If your organization adopts Windows SharePoint Services as a Web-based collaboration platform, the answer is a resounding yes! Windows SharePoint Services provides a new collaboration platform for Office users, and the platform is especially attractive for Office System users. From a developer's perspective, the Windows SharePoint Services platform is extensible on both the server side with custom Web Parts and on the client-side with applications that consume Web services provided by SharePoint Products and Technologies. The sample application provided with this article shows you how to consume Web services from Windows SharePoint Services in an Outlook Add-in written using Microsoft Visual Basic .NET 2003. The sample Add-in allows a user to import Contact items from a SharePoint site and to export Outlook Contact items to an existing SharePoint site. The user interface for the Outlook Add-in for SharePoint Products and Technologies is a custom command bar shown in Figure 5.
Figure 5. Add-in for importing contacts from SharePoint lists
Note Due to space considerations, this article does not discuss all the details of writing a managed code Add-in for Outlook. For a complete discussion of writing a managed Add-in for Outlook, see the following articles:
The sample application provides an example of an Outlook Add-in created with Visual Basic .NET 2003. Unlike the native import and export functionality for a Contacts list, the sample application lets you import Contact items from a SharePoint list to any Contacts folder in your profile. It does not limit you to importing Contacts to the Outlook folder linked to the SharePoint list. All Contact items in a native Outlook folder linked to a SharePoint list are read-only items. These read-only folders are always contained in a Personal Folders file (.PST) rather than a subfolder of your Mailbox. The sample application improves upon the native folders linked to a SharePoint list by allowing you to import Contacts from a SharePoint list directly into a read-write Contacts folder as shown in Figure 6. This custom Contacts folder can be a subfolder of your Mailbox, so mobile Exchange users using the cached Exchange mode, always have their exported Contacts from the SharePoint list in their Mailbox. Note that unlike the default linked PST-based Contact folder, the sample application does not automatically perform one-way synchronization from the SharePoint list to Outlook. However, you can extend the sample application to support two-way synchronization.
Figure 6. Contacts exported from a SharePoint list into Outlook (Click picture to see larger image)
Before you review the sample code, you need to download, install, and then test the Microsoft Office Outlook 2003 Add-in for Microsoft SharePoint Products and Technologies. You also must ensure that you have installed the Outlook 2003 Primary Interop Assembly (PIA). Fortunately, the PIAs that enable .NET programmability are included with all editions of the Microsoft Office System CD.
Note You may need to install the Outlook PIA before proceeding if you did not choose the Complete option during installation.
To install the Outlook 2003 PIA:
- On a computer with Microsoft Visual Studio® .NET 2003 and the Office System installed, on the Start menu, point to Control Panel, and then click Add or Remove Programs.
- In the list of currently installed programs, click Microsoft Office Professional Edition 2003, and then click Change.
- In the Microsoft Office 2003 Setup window, click Add or Remove Features, and then click Next.
- Check the Choose advanced customization of applications box and click Next.
- Expand the Microsoft Office Outlook node.
- Click the .NET Programmability Support drop-down and select Run from My Computer as shown in Figure 7.
- Click Update to install the selected option.
Figure 7. Installing .NET Programmability Support (Click picture to see larger image)
After downloading the sample file, extract the sample to the Visual Studio Projects folder. By default, this is located in the My Documents folder. You should now have a folder named WSSAddin folder under the Visual Studio Projects folder.
The WSSAddin folder contains the Visual Basic managed Add-in project. The name of the assembly is WSSAddin. To view this project, open WSSAddin.sln located in the project folder. This folder also contains a subfolder with the name WSSAddinSetup. This contains the managed WSSAddin setup project for deploying the completed solution.
To install the solution:
Note Before installing the managed WSSAddin Add-in .DLL file, close Outlook.
- In the project folder, double-click WSSAddin.sln to open it in Visual Studio .NET. In the Solution Explorer, note the two projects: WSSAddin and WSSAddinSetup.
- First, rebuild the managed Add-in by right-clicking on the WSSAddin project and then clicking Rebuild.
- Next, build the managed Add-in setup project by right-clicking on the WSSAddinSetup project and then clicking Build.
- To install the Add-in, right-click WSSAddinSetup and then click Install. This launches the Outlook Add-in for Windows SharePoint Services Setup Wizard.
- Click Next.
- In the Select Installation Folder dialog box, accept the default or supply a different installation folder, and then click Next.
- Click Next to install Outlook Add-in for Windows SharePoint Services.
Working with the Lists Web Service in Windows SharePoint Services
In the sample application, you work with the Lists Web service. The Lists Web service provides several methods for working with lists and list data. From an Outlook perspective, think of List objects as the equivalent of Folder objects and List items as the equivalent of Outlook Item objects such as a ContactItem. A SharePoint site can contain many Lists, including Contacts, Tasks, Events, Announcements, and so forth. In this example, we exclusively use the Contacts List. A sample Contacts List is shown in Figure 8. You can extend the sample code shown in the sample application to work with any SharePoint list and its list items. For more information about the Lists Web service, see the Microsoft SharePoint Products and Technologies Software Development Kit (SDK).
Figure 8. A default Contacts list on a SharePoint site (Click picture to see larger image)
To set a reference to the Lists Web service
- In Visual Studio .NET, click Project, and then click Add Web Reference.
Figure 9. Adding a Web Reference (Click picture to see larger image)
- As show in Figure 9, type the URL to the Lists Web service in the URL box using the following format:
- Click Go.
- In the Web reference name box, type WSS.
- Click Add Reference.
Using the Lists Web Service
Once you add a reference to the Lists Web service, you can use the Lists Web Service to import and export Contact information. The Lists Web service exposes Web methods that are essential for working with SharePoint lists. The sample application uses the GetLists, GetListItems, and UpdateListItems methods. The following table describes all the methods of the Lists Web service.
Table 7. Methods of the Lists Web service
|AddAttachment||Adds an attachment to the specified list item in the specified list.|
|AddList||Creates a list in the current site based on the specified name, description, and list template ID.|
|DeleteAttachment||Removes the attachment from the specified list item.|
|DeleteList||Deletes the specified list.|
|GetAttachmentCollection||Returns a list of the URLs for attachments to the specified item.|
|GetList||Returns a schema for the specified list.|
|GetListAndView||Returns the list and view schemas for the specified list.|
|GetListCollection||Returns the names and GUIDs for all the lists in the site.|
|GetListItemChanges||Returns changes made to the list since the specified date and time.|
|GetListItems||Returns information about items in the list based on the specified query.|
|UpdateList||Updates a list based on the specified field definitions and list properties.|
|UpdateListItems||Updates the specified items in a list on the current site.|
Importing SharePoint Contact Items
The cbbImport_Click procedure contains the code to import a Contacts list from a SharePoint site to a Contacts folder in Outlook. The first task in this procedure is to obtain the URL for the SharePoint site as follows:
strKey = "Software\Microsoft\Office\Outlook\Addins\" _ & m_ProgID Reg = Registry.CurrentUser.CreateSubKey(strKey) strURL = Reg.GetValue("URL", "http://") Dim strMsg As String = _ "Type the URL for your SharePoint Site:" & vbCrLf _ & "http://server_name[/sites/site_name]" strURL = InputBox(strMsg, APP_TITLE, strURL) If strURL = "" Then Exit Try Else Reg.SetValue("URL", strURL) End If
Once we obtain a URL, we need to determine the Outlook folder to which to import the Contact items. The PickFolder method obtains this folder, and the code checks the DefaultItemType of the folder to ensure that the user selects a Contacts folder:
Dim oFolder As Outlook.MAPIFolder = m_olApp.Session.PickFolder If oFolder Is Nothing Then Exit Try End If If oFolder.DefaultItemType <> _ Outlook.OlItemType.olContactItem Then MsgBox("You can only import to a Contacts folder!", _ MsgBoxStyle.Exclamation, APP_TITLE) Exit Try End If
The next step is to assign a valid URL to the ListsWS object on the SharePoint site and to ensure that the Web service uses the default user credentials for authentication:
'Assign the URL for Lists Web service ListWS.Url = strURL & "/_vti_bin/lists.asmx" 'Use Windows credentials ListWS.Credentials = _ System.Net.CredentialCache.DefaultCredentials 'Set timeout to 20 seconds ListWS.Timeout = 20000
If the URL for ListWS object is valid and does not throw an error that is trapped in the Catch block, the code calls the GetListItems method of the Lists Web service. To ensure that all default fields of a List item are returned, you must create an XMLNode object named nodViewFields. The nodViewFields object is passed to the GetListItems method as the ViewFields argument. ViewFields is an element that specifies which fields to return in GetListItems call. Be aware that the field names in the XML for nodViewFields are case-sensitive. GetListItems returns its results in nodListItems, which is a System.Xml.XmlNode object. Once you have nodListItems, you can convert the XML into a System.Data.DataSet object, and then enumerate the rows in the DataSet object to create or modify Outlook Contact items. The code determines if the Outlook Contact already exists in the folder by performing a Restrict using the User2 field for the Outlook contact item. User2 contains the URL for editing the contact, and is guaranteed to be unique. If an existing Outlook Contact item is found with the URL contained in User2, then the code uses GetFirst to obtain the existing ContactItem. Otherwise, a new ContactItem is created in the Outlook folder represented by oFolder. The URL required to edit the Contact on the SharePoint list appears in the body of the Contact as shown in Figure 10. Although the following listing is lengthy, it shows you all the steps required to create or update Outlook Contact items from a Contacts list on a SharePoint site. Not shown below, the CheckNull function ensures that a null value is not assigned to an Outlook property.
Figure 10. An imported contact (Click picture to see larger image)
'Create new XmlDocument Dim xmlDoc = New System.Xml.XmlDocument 'nodViewFields node contains ViewFields nodViewFields = _ xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "") 'ViewFields will be returned by GetListItems 'All Names are case-sensitive nodViewFields.InnerXml = _ "<FieldRef Name='ID'/><FieldRef Name='Title'/>" _ & "<FieldRef Name='FirstName'/><FieldRef Name='FullName'/>" _ & "<FieldRef Name='Email'/><FieldRef Name='Company'/>" _ & "<FieldRef Name='JobTitle'/><FieldRef Name='WorkPhone'/>" _ & "<FieldRef Name='HomePhone'/><FieldRef Name='CellPhone'/>" _ & "<FieldRef Name='WorkFax'/><FieldRef Name='WorkAddress'/>" _ & "<FieldRef Name='WorkCity'/><FieldRef Name='WorkState'/>" _ & "<FieldRef Name='WorkZip'/><FieldRef Name='WorkCountry'/>" _ & "<FieldRef Name='WebPage'/><FieldRef Name='Comments'/>" 'GetListItems retrieves items in Contacts List nodListItems = _ ListWS.GetListItems _ ("Contacts", "", Nothing, nodViewFields, "1000", Nothing) If nodListItems.HasChildNodes Then 'ChildNodes(1) contains list items nodRows = nodListItems.ChildNodes(1) If CInt(nodRows.Attributes("ItemCount").Value) = 0 Then fWait.Close() MsgBox("No SharePoint Contacts available to import!", _ MsgBoxStyle.Critical, APP_TITLE) Exit Try End If 'Use StringReader with nodRows.OuterXML Dim xmlSR As System.IO.StringReader = _ New System.IO.StringReader(nodRows.OuterXml) 'Read xmlSR into DataSet myDS.ReadXml (xmlSR) 'Tables(1) contains rows Dim myTable As System.Data.DataTable = myDS.Tables(1) 'Enumerate contacts For intRow = 0 To myTable.Rows.Count - 1 'ID contains the Primary Key for the item in the SharePoint list strID = myTable.Rows(intRow).Item("ows_ID") 'Test to determine if a contact already exists Dim oContact As Outlook.ContactItem Dim colItems As Outlook.Items = oFolder.Items 'User2 property contains URL to SharePoint list item Dim strRestrict As String = "[User2] = '" _ & strURL & "/Lists/Contacts/EditForm.aspx?ID=" _ & strID & "'" Dim colRestrict As Outlook.Items colRestrict = colItems.Restrict(strRestrict) If colRestrict.Count Then 'Contact already exists oContact = colRestrict.GetFirst Else 'Create a contact for the row oContact = _ oFolder.Items.Add("IPM.Contact") End If With oContact For intCol = 0 To myTable.Columns.Count - 1 strColumnName = myTable.Columns(intCol).ColumnName strValue = CheckNull(myTable.Rows(intRow).Item(intCol)) Select Case strColumnName Case "ows_ID" 'ID contains the Primary Key for the item in the SharePoint list strID = strValue Case "ows_Title" .LastName = strValue Case "ows_FirstName" .FirstName = strValue Case "ows_FullName" If strValue <> "" Then .FullName = strValue End If Case "ows_Company" .CompanyName = strValue Case "ows_Email" .Email1Address = strValue Case "ows_JobTitle" .JobTitle = strValue Case "ows_WorkAddress" .BusinessAddressStreet = strValue Case "ows_WorkCity" .BusinessAddressCity = strValue Case "ows_WorkState" .BusinessAddressState = strValue Case "ows_WorkZip" .BusinessAddressPostalCode = strValue Case "ows_WorkCountry" .BusinessAddressCountry = strValue Case "ows_WorkPhone" .BusinessTelephoneNumber = strValue Case "ows_HomePhone" .HomeTelephoneNumber = strValue Case "ows_CellPhone" .MobileTelephoneNumber = strValue Case "ows_WorkFax" .BusinessFaxNumber = strValue Case "ows_WebPage" intSep = InStr(strValue, ",", CompareMethod.Text) If intSep > 1 Then strValue = _ Microsoft.VisualBasic.Left _ (strValue, intSep - 1) Else strValue = "" End If .WebPage = strValue Case "ows_Comments" 'Add the URL for the Contact in the SharePoint list 'to Body of Contact strComments = "<" & strURL & _ "/Lists/Contacts/EditForm.aspx?ID=" _ & strID & ">" & vbCrLf & strValue .Body = strComments End Select Next 'Add ID to User1 property .User1 = strID 'Add URL to List Item to User2 property .User2 = strURL & _ "/Lists/Contacts/EditForm.aspx?ID=" & strID 'Finally, save the ContactItem .Save() End With Next End If
Exporting Outlook Contact Items
The cbbExport_Click procedure contains the code to export Contact items from Outlook to a SharePoint list. The Export Contacts button is only enabled when a user navigates to an Outlook Contacts folder. The Export Contacts button only exports the selected Contacts in the folder view. This method is somewhat awkward, but uses this design because Outlook does not expose an AddressBook method. If you experiment with the Import Contacts button on the Contacts page of the SharePoint site, you may notice that the Outlook Security Warning dialog box shown in Figure 2 appears after you select recipients in the Select Users to Import window. The code in cbbExport does not cause the Security Warning to appear because all Outlook objects in the Add-in are trusted since they derive from the Application object passed in the OnConnection event of the WSSAddin.Connect class.
The purpose of the code in cbbExport is to create an XML fragment that uses CAML to pass as a parameter for the UpdateListItems method of ListsWS object. For more information about CAML, see the Microsoft SharePoint Products and Technologies Software Development Kit (SDK). To create CAML, the code creates an XML.XMLDocument object named xmlDoc, and then creates a root element named Batch. The code then creates child elements named Method for each selected ContactItem in the Contacts folder. Each Method element must have an ID and Cmd attribute. Each Method element represents a selected ContactItem. After you create the Method element, Field elements contain the values for Property values such as FirstName, LastName, CompanyName, and so forth for each selected ContactItem. If the ContactItem contains a value for User1, it is assumed that the Contact already exists in the SharePoint list. The Cmd attribute changes to Update instead of New if the item already exists in the SharePoint list based upon values contained in User1 and User2. Finally, an XML.XMLNode object named nodCAML is created from the InnerXML of xmlDoc. When you pass nodCAML to the UpdateListItems method, it returns a nodResult that contains extended error information about the UpdateListItems call:
'Create new XmlDocument xmlDoc = New Xml.XmlDocument 'create Root element xmlRoot = xmlDoc.CreateElement("Batch") xmlRoot.SetAttribute("OnError", "Continue") xmlRoot.SetAttribute("ListVersion", "1") xmlDoc.AppendChild (xmlRoot) 'Iterate over collection of selected items For intCounter = 1 To colSelectedItems.Count 'Only process contact items 'Contacts folder can contain DistList and Contact Items If InStr(colSelectedItems.Item _ (intCounter).MessageClass, "IPM.Contact", _ CompareMethod.Text) Then oContact = colSelectedItems(intCounter) 'Create a 'New' Method element for CAML xmlMethod = xmlDoc.CreateElement("Method") xmlMethod.SetAttribute("ID", "Items" & (intCounter + 1)) If oContact.User1 = "" Then xmlMethod.SetAttribute("Cmd", "New") Else xmlMethod.SetAttribute("Cmd", "Update") End If xmlRoot.AppendChild (xmlMethod) 'User1 contains the unique ID 'User2 contains URL for EditForm.aspx If oContact.User1 <> "" And _ Left(oContact.User2, Len(strURL)) = strURL Then 'User1 contains existing ID xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "ID") xmlField.InnerXml = CheckNothing(oContact.User1) xmlMethod.AppendChild (xmlField) End If 'Create multiple Field elements 'FirstName xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "FirstName") xmlField.InnerXml = CheckNothing(oContact.FirstName) xmlMethod.AppendChild (xmlField) 'Title xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "Title") xmlField.InnerXml = CheckNothing(oContact.LastName) xmlMethod.AppendChild (xmlField) 'FullName xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "FullName") xmlField.InnerXml = CheckNothing(oContact.FullName) xmlMethod.AppendChild (xmlField) 'Email xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "Email") xmlField.InnerXml = CheckNothing(oContact.Email1Address) xmlMethod.AppendChild (xmlField) 'Company xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "Company") xmlField.InnerXml = CheckNothing(oContact.CompanyName) xmlMethod.AppendChild (xmlField) 'JobTitle xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "JobTitle") xmlField.InnerXml = CheckNothing(oContact.JobTitle) xmlMethod.AppendChild (xmlField) 'WorkAddress xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkAddress") xmlField.InnerXml = CheckNothing(oContact.BusinessAddressStreet) xmlMethod.AppendChild (xmlField) 'WorkCity xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkCity") xmlField.InnerXml = CheckNothing(oContact.BusinessAddressCity) xmlMethod.AppendChild (xmlField) 'WorkState xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkState") xmlField.InnerXml = CheckNothing(oContact.BusinessAddressState) xmlMethod.AppendChild (xmlField) 'WorkZip xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkZip") xmlField.InnerXml = _ CheckNothing(oContact.BusinessAddressPostalCode) xmlMethod.AppendChild (xmlField) 'WorkCountry xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkCountry") xmlField.InnerXml = CheckNothing(oContact.BusinessAddressCountry) xmlMethod.AppendChild (xmlField) 'WorkPhone xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkPhone") xmlField.InnerXml = CheckNothing(oContact.BusinessTelephoneNumber) xmlMethod.AppendChild (xmlField) 'HomePhone xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "HomePhone") xmlField.InnerXml = CheckNothing(oContact.HomeTelephoneNumber) xmlMethod.AppendChild (xmlField) 'CellPhone xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "CellPhone") xmlField.InnerXml = CheckNothing(oContact.MobileTelephoneNumber) xmlMethod.AppendChild (xmlField) 'WorkFAX xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WorkFax") xmlField.InnerXml = CheckNothing(oContact.BusinessFaxNumber) xmlMethod.AppendChild (xmlField) 'WebPage xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "WebPage") xmlField.InnerXml = CheckNothing(oContact.WebPage) xmlMethod.AppendChild (xmlField) 'Comments xmlField = xmlDoc.CreateElement("Field") xmlField.SetAttribute("Name", "Comments") xmlField.InnerXml = CheckNothing(oContact.Body) xmlMethod.AppendChild (xmlField) End If Next strXML = xmlDoc.InnerXml 'Create XMLNode for CAML Fragment xmlCAML = New Xml.XmlDocument nodCAML = xmlCAML.CreateNode(XmlNodeType.DocumentFragment, "", "CAML", "") nodCAML.InnerXml = strXML 'nodResult returns extended error information nodResult = ListWS.UpdateListItems("Contacts", nodCAML)
The listing above uses two functions to ensure that the CAML is well formed XML and does not cause an error when the UpdateListItems method is called. CheckNothing and CreateValidXML are shown below:
Private Function CheckNothing(ByVal oValue) As String On Error Resume Next If oValue Is Nothing Then CheckNothing = "" Else 'Use the CreateValidXML function to escape 'invalid XML characters &, ', >, <, "" CheckNothing = CreateValidXML(oValue) End If End Function Private Function CreateValidXML(ByVal strXML As String) As String On Error Resume Next strXML = Regex.Replace(strXML, "&", "&") strXML = Regex.Replace(strXML, "'", "'") strXML = Regex.Replace(strXML, "<", "<") strXML = Regex.Replace(strXML, ">", ">") strXML = Regex.Replace(strXML, Chr(34), """) CreateValidXML = strXML End Function
Changes to the Outlook object model are limited in scope. However, a new and improved security model allows Add-in developers to write code without the restrictions imposed by the Outlook E-mail Security Update. Code security is still enforced for code that operates outside the installed Add-ins, VBA, and code behind published forms. Exchange administrators can continue to impose a more granular security model on the Outlook object model. Windows SharePoint Services provides a new collaboration platform that works best for Office System users. Using the Microsoft SharePoint Products and Technologies SDK, you can extend Outlook to integrate with SharePoint sites. The sample application discussed in this article is one example of what you can accomplish when you integrate Outlook with SharePoint Products and Technologies.
About the Author
Randy Byrne is the President of Micro Eye, Inc., a Microsoft Certified Partner located in the San Francisco Bay Area. Randy is an Outlook MVP and the author of Building Applications with Microsoft Outlook 2000 and Building Applications with Microsoft Outlook Version 2002 published by Microsoft Press.