Developing powerful Outlook add-ins with Visual Studio (VB.NET, C#)
With the recent Beginning Outlook Development series complete, we thought it would be a good idea to do a demo that shows how easy/simple/relatively painless it is to build a powerful Outlook add-in with Visual Studio (VB.NET, C#). Of course, we’ll be using Add-in Express for Office and .NET to get the job done.
In this article (and its accompanying video), I’ll show you to build…
- An advanced Outlook form region that shows the selected mail item’s mail header in the explorer window by using the SelectionChange event.
- An advanced Outlook form region that displays the current mail item’s folder path in the inspector window. When the user clicks the path, they are shown this folder in the explorer window.
- Two custom Outlook ribbons containing toggle buttons that control the display of the forms (items 1 & 2).
Of course, I’ll include the code that ties everything together. I don’t mind saying, the code is so good… you can steal it straight from here and use it today. If you have other logic you want to implement, I think you will still find this code quite useful.
Create a professional Outlook add-in with little coding
Prerequisite – the Visual Studio project created with Add-in Express for Office and .NET.
I don’t think I need to explain how to do this. Just create an ADX COM Add-in project that targets Outlook 2007, 2010 and Outlook 2013. I’m calling my project OutlookDemo… because that is what it is. Let’s get going.
- Building the Outlook add-in UI
- Coding the features
- Mail Header form
- Folder Path form
- Add the MAPI methods
Building the Outlook add-in UI
The UI for this Outlook addin consists of two ribbons and two forms. We’re going to move through them fast so we can move to the code discussion.
Custom Outlook ribbons
We need two ribbons. One for the Outlook explorer window and the other for the Outlook Inspector windows. Both will target Outlook mail items.
To create the ribbons, open the AddinModule in design view. Then complete the following steps:
- Add an ADXRibbonTab to the design surface and set the following properties
- Name = ExplorerRibbon
- Caption = HEADER OPTIONS
- Ribbons = OutlookExplorer
- Select the ExplorerRibbon and use the visual designer’s toolbar to add an ADXRibbonGroup to the tab. Change the group’s Caption to My Explorer Actions.
- Select the ADXRibbonGroup and use the toolbar once again to add an ADXRibbonButton to the group. The button’s properties requiring changes are as follows:
- Name = RibbonButtonViewHeader
- Caption = View Mail Header
- Size = Large
- ToggleButton = True
This takes care of the ribbon for the explorer window. We have a nice Outlook ribbon with a single toggle button. Let’s create the inspector’s ribbons.
- Add another ADXRibbonTab to the AddInModule’s design surface and set the following properties
- Name = InspectorRibbon
- Caption = MY ACTIONS
- Ribbons = OutlookMailRead
- Add an ADXRibbonGroup to the tab. Change the group’s Caption to My Inspector Actions.
- Select the ADXRibbonGroup and add an ADXRibbonButton to the group. Change button’s properties to:
- Name = RibbonButtonToGo
- Caption = Go To Item Folder
- Size = Large
- ToggleButton = True
This ribbon will now display anytime an Inspector displays a mail item. Let’s now create the custom Outlook forms that these buttons control.
The Mail Header form
This form consists of a single text box control that fills the entire form real estate. The text box will display a mail item’s mail header at the appropriate time. So, add a new ADX Outlook form to the add-in project and name it MailHeader.
Add a TextBox to the design surface and configure it according the image below.
We are done with this custom form for now. We’ll come back to it as we have some code to write.
The Folder Path form
This Outlook form will show the folder path for a mail item’s parent folder. All you need is a Panel and LinkLabel control. Place the link label inside the panel and set the properties as show here:
The UI is complete. Art class is over. Let’s code.
Coding the features
The majority of the add-in’s code resides in the AddinModule. But we allow the Outlook form regions to play their part as well.
The AddInModule code
We’ll start with the AddInModule. It contains the following routines:
- RibbonButtonViewHeader_OnClick
- GetMailHeader
- ExplorerSelectionChange
- RibbonButtonGoTo_OnClick
The first three bullets relate to the logic required for the Explorer window and the MailHeader form. The last one is for the Inspector windows and the FolderPath form.
RibbonButtonViewHeader_OnClick
This is the click even for the button in the ExplorerRibbon control. I recommend that you copy and paste this code into your Outlook add-in. Just open the AddinModule’s code view and paste it!
Private Sub RibbonButtonViewHeader_OnClick(sender As Object, _ control As IRibbonControl, pressed As Boolean) Handles _ RibbonButtonViewHeader.OnClick ' handle all Explorer windows For i As Integer = 0 To ExplorerCollectionItem.FormInstanceCount - 1 If pressed Then ' show the form ExplorerCollectionItem.FormInstances(i).RegionState = _ AddinExpress.OL.ADXRegionState.Normal Else ' hide the form ExplorerCollectionItem.FormInstances(i).RegionState = _ AddinExpress.OL.ADXRegionState.Hidden End If Next End Sub
This event loops through all ExplorerCollectionItem instances and either displays or hides the MailHeader form. It all depends on the value of the Pressed parameter.
GetMailHeader
This method returns the Outlook mail header text of the mail item selected in the Outlook Explorer window.
Private Function GetMailHeader() As String Dim explorer As Outlook._Explorer = Nothing Dim selection As Outlook.Selection = Nothing Dim mailItem As Outlook._MailItem Dim item As Object = Nothing Dim headerText As String = String.Empty Try explorer = OutlookApp.ActiveExplorer() 'Explorer.Selection fires an exception for a top-level folder selection = explorer.Selection item = selection.Item(1) If TypeOf item Is Outlook._MailItem Then mailItem = CType(item, Outlook._MailItem) If Me.HostMajorVersion >= 12 Then ' Outlook 2007 and higher ' use late binding Dim propertyAccessor As Object = Nothing propertyAccessor = mailItem.GetType().InvokeMember( _ "PropertyAccessor", Reflection.BindingFlags.GetProperty, Nothing, mailItem, New Object() {}) headerText = propertyAccessor.GetType().InvokeMember( _ "GetProperty", Reflection.BindingFlags.InvokeMethod, Nothing, propertyAccessor, _ New Object() {"https://schemas.microsoft.com/mapi/proptag/0x007D001E"}).ToString() Marshal.ReleaseComObject(propertyAccessor) Else ' use Extended MAPI Dim itemPtr As IntPtr = IntPtr.Zero Dim retVal As String = String.Empty Dim propAddressPtr As IntPtr = IntPtr.Zero Try itemPtr = Marshal.GetIUnknownForObject(mailItem.MAPIOBJECT) If (MAPI.HrGetOneProp(itemPtr, MAPI.PR_TRANSPORT_MESSAGE_HEADERS_A, propAddressPtr) = MAPI.S_OK) Then Dim emails As IntPtr = IntPtr.Zero Dim propValue As SPropValue = CType(Marshal.PtrToStructure(propAddressPtr, GetType(SPropValue)), SPropValue) headerText = Marshal.PtrToStringAnsi(New IntPtr(propValue.Value)) End If Finally If propAddressPtr <> IntPtr.Zero Then MAPI.MAPIFreeBuffer(propAddressPtr) If itemPtr <> IntPtr.Zero Then Marshal.Release(itemPtr) End Try End If End If Catch Finally If explorer IsNot Nothing Then Marshal.ReleaseComObject(explorer) If selection IsNot Nothing Then Marshal.ReleaseComObject(selection) If item IsNot Nothing Then Marshal.ReleaseComObject(item) End Try Return headerText 'See this article for more info regarding accessing the mail header 'https://blogs.msdn.com/b/zainnab/archive/2008/07/01/using-visual-studio-2008-vsto-outlook-to-pull-out-rfc-822-header-data.aspx End Function
It begins by referencing the ActiveExplorer and using this object to reference the selected email. A Selection object can contain multiple Outlook items of various types. So, we make the assumption we only want the first item. However, we don’t assume we have a MailItem, which is why we check its type before continuing.
If we are working with Outlook 2007 or later, we move on and utilize a PropertyAccessor combined with late binding, reflection, and extended MAPI to retrieve the mail header text.
Note. To learn more about extended MAPI, please see the Add the MAPI methods section at the end of this post. You will need to add these methods to your Outlook addin for the GetMailHeader function to work. They are out-of-scope for today’s discussion but they are well-worth knowing and mastering. I have included links to help you get started. Also, my thanks to Dmitry Kostochko for his help in refining the code of this Outlook plug-in so that it is truly version neutral.
ExplorerSelectionChange
This event occurs when the user selects a different folder item within an Outlook Explorer window (we can also make it happen with code… just FYI). When the user selects a different item, we want to update the mail header text in the MailHeader form.
Private Sub adxOutlookEvents_ExplorerSelectionChange( _ sender As Object, explorer As Object) Handles _ adxOutlookEvents.ExplorerSelectionChange Dim ExplorerForm As MailHeader = Nothing ExplorerForm = TryCast( _ ExplorerCollectionItem.GetCurrentForm( _ AddinExpress.OL.EmbeddedFormStates.Visible), _ MailHeader) If ExplorerForm IsNot Nothing Then ExplorerForm.HeaderText.Text = GetMailHeader() End If End Sub
The event references the ExplorerCollectionItem‘s currently visible form. And if it exists, we updated the form’s text box with a call to GetMailHeader. Nothing to it.
RibbonButtonGoTo_OnClick
This event is pretty much the same as RibbonButtonViewHeader_OnClick.
Private Sub RibbonButtonGoTo_OnClick( _ sender As Object, control As IRibbonControl, pressed As Boolean) _ Handles RibbonButtonGoTo.OnClick ' handle all Inspector windows For i As Integer = 0 To InspectorCollectionItem.FormInstanceCount - 1 If pressed Then ' show the form InspectorCollectionItem.FormInstances(i).RegionState = _ AddinExpress.OL.ADXRegionState.Normal Else ' hide the form InspectorCollectionItem.FormInstances(i).RegionState = _ AddinExpress.OL.ADXRegionState.Hidden End If Next End Sub
The only difference is the use of the InspectorCollectionItem instead of the ExplorerCollectionItem.
Mail Header form
We’ll complete this Outlook form first because it doesn’t have much code when compared to FolderPath. All this form needs to do is respond to the ADXBeforeFormShow event.
ADXBeforeFormShow
This event fires just before the form displays. It’s the place to check the status of the ribbon’s toggle button and show or hide the form accordingly.
Private Sub MailHeader_ADXBeforeFormShow() Handles MyBase.ADXBeforeFormShow If OutlookDemo.AddinModule.CurrentInstance.RibbonButtonViewHeader.Pressed Then Me.RegionState = ADXRegionState.Normal Else Me.RegionState = ADXRegionState.Hidden End If End Sub
This strategy is easy to accomplish because we can get to the ribbon and its controls via the AddinModule class.
Folder Path form
This custom Outlook form displays within an Inspector window for Outlook mail items only. It shows the mail items folder path and allows the user to navigate to the folder with a simple click.
ADXBeforeFormShow
This works exactly the same as with the MailHeader form.
Private Sub FolderPath_ADXBeforeFormShow() Handles MyBase.ADXBeforeFormShow If OutlookDemo.AddinModule.CurrentInstance.RibbonButtonGoTo.Pressed Then Me.RegionState = ADXRegionState.Normal Else Me.RegionState = ADXRegionState.Hidden End If End Sub
Load event
This event is just what we need to retrieve the mail item’s folder path and display it in the LabelLink control. First, we need to create a private class member called FolderEntryID. This member is stores the folder’s EntryID property for later use.
Private FolderEntryID As String Private Sub FolderPath_Load(sender As Object, e As EventArgs) _ Handles MyBase.Load Dim objFolder As Outlook.MAPIFolder = Nothing Dim objItem As Object = Nothing FolderEntryID = String.Empty Me.FolderPathLink.Text = String.Empty Try objItem = Me.InspectorObj.CurrentItem If objItem IsNot Nothing Then Try objFolder = objItem.Parent If objFolder IsNot Nothing Then FolderEntryID = objFolder.EntryID Me.FolderPathLink.Text = GetFolderPath(objFolder) End If Finally Marshal.ReleaseComObject(objItem) End Try End If Catch End Try End Sub
The method references the CurrentItem of the form’s InspectorObj property. The CurrentItem should be a MailItem but we don’t really need to attempt a cast. We only need to get the item’s Parent property. The Parent is the item’s folder object. When we have the folder object, we store its EntryID for later and call GetFolderPath to display the path in the form’s LabelLink control.
GetFolderPath method
This method returns the folder path of the passed folder object… nothing more, nothing less.
Function GetFolderPath(ByVal objFolder As Outlook.MAPIFolder) As String Dim strFolderPath As String Dim objChild As Outlook.MAPIFolder Dim objParent As Outlook.MAPIFolder strFolderPath = "\" + objFolder.Name objChild = objFolder While objChild IsNot Nothing Try objParent = objChild.Parent Catch objParent = Nothing Finally If objChild IsNot Nothing Then Marshal.ReleaseComObject(objChild) End If End Try If objParent Is Nothing Then Exit While strFolderPath = "\" + objParent.Name + strFolderPath objChild = objParent End While Return "\" + strFolderPath End Function
The method starts with the passed folder and attempts to move up the “parent” parent chain to build the Outlook folder path. As long as the method continues to find a folder in the Parent property, it continues… otherwise it exists the While loop and returns the path.
FolderPathLink_LinkClicked event
This event navigates the user to the item’s parent folder in Outlook Explorer. It then activates the Explorer windows so that it moves to the foreground.
Private Sub FolderPathLink_LinkClicked( _ sender As Object, e As Windows.Forms.LinkLabelLinkClickedEventArgs) _ Handles FolderPathLink.LinkClicked Dim olApp As Outlook._Application = Me.OutlookAppObj Dim parentFolder As Outlook.MAPIFolder = Nothing Dim ns As Outlook._NameSpace = Nothing Dim activeExplorer As Outlook._Explorer = Nothing Try ns = olApp.GetNamespace("MAPI") parentFolder = ns.GetFolderFromID(FolderEntryID, Type.Missing) activeExplorer = olApp.ActiveExplorer() activeExplorer.CurrentFolder = parentFolder activeExplorer.Activate() Catch Finally If ns IsNot Nothing Then Marshal.ReleaseComObject(ns) If activeExplorer IsNot Nothing Then Marshal.ReleaseComObject(activeExplorer) If parentFolder IsNot Nothing Then Marshal.ReleaseComObject(parentFolder) End Try End Sub
To make this function work, we reference the MAPI namespace and call its GetFolderFromID method. Lucky for us, we stored it early in the private class member (FolderEntryID). After we retrieve the folder, we set the CurrentFolder of the ActiveExplorer to the folder and activate it. I love it when a plan comes together.
Add the MAPI methods
The following Extended MAPI routines are required for this Outlook add-in to work correctly. They are out of the scope of today’s discussion but I recommend that you investigate the following links to learn more:
Don’t forget to paste this code into your solution!
#Region "Extended MAPI routines" <StructLayout(LayoutKind.Sequential)> Friend Structure SPropValue Dim ulPropTag As UInteger Dim dwAlignPad As UInteger Dim Value As Long End Structure Class MAPI Friend Shared S_OK As Integer = 0 Friend Shared PR_TRANSPORT_MESSAGE_HEADERS_A As UInteger = &H7D001E <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, _ EntryPoint:="HrGetOneProp@12")> Private Shared Function HrGetOneProp32(pmp As IntPtr, _ ulPropTag As UInteger, <Out> ByRef ppProp As IntPtr) As Integer End Function <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, _ EntryPoint:="MAPIFreeBuffer@4")> Private Shared Sub MAPIFreeBuffer32(lpBuffer As IntPtr) End Sub <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, _ EntryPoint:="HrGetOneProp")> Private Shared Function HrGetOneProp64(pmp As IntPtr, _ ulPropTag As UInteger, <Out> ByRef ppProp As IntPtr) As Integer End Function <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, _ EntryPoint:="MAPIFreeBuffer")> Private Shared Sub MAPIFreeBuffer64(lpBuffer As IntPtr) End Sub Friend Shared Function HrGetOneProp(pmp As IntPtr, _ ulPropTag As UInteger, <Out> ByRef ppProp As IntPtr) As Integer If (IntPtr.Size = 8) Then Return HrGetOneProp64(pmp, ulPropTag, ppProp) Else Return HrGetOneProp32(pmp, ulPropTag, ppProp) End If End Function Friend Shared Sub MAPIFreeBuffer(lpBuffer As IntPtr) If (IntPtr.Size = 8) Then MAPIFreeBuffer64(lpBuffer) Else MAPIFreeBuffer32(lpBuffer) End If End Sub End Class #End Region
***
Believe it or not, this is a minor amount of code that generates some major features. I bet you could put these to use today just as a simple, loyal, Outlook user. But the benefits in this sample go beyond the extended Outlook features it provides. The real benefit are in the strategies presented in how to access the custom UI elements and the Outlook items.
Available downloads:
This sample Outlook add-in was developed using Add-in Express for Office and .net:
2 Comments
I would like implement outlook 2010 Add-In that will help to call to a outlook contact by my local application.
Scenario:
Need a “call to dial” application that help to call a number of outlook contact.
When click on outlook contact, then Add-In must be get called & divert that call to my local application.
e.g. MS Lync Add-In works with Outlook contacts to call a selected contact’s number.
I have seen many video & studied many tutorial and but didnt get info that how to implement desired scenario.
I used below approaches
With Add-In Express: 1. MS Visual Studio 2013-> Visual C#-> Office-> Office Add-In.
With VSTO: 2. MS Visual Studio 2013-> Other Project Type-> Extensibility->ADX Add->In
Thanks
You can add a custom button to the context menu and also to the ribbon so that the user clicks the button to send required data to your application. If you have problems with doing this, contact us using the contact form at https://www.add-in-express.com/support/askus.php.