Migrating an Outlook Client to .NET Framework 4 in Visual Studio 2010

Early this year we built a business application for order management for Northwind Traders on the Office and SharePoint platform using Visual Studio 2008 and Office & SharePoint 2007. If you missed them:

The solution consists of an Outlook client that pulls up order history when a customer email arrives in the sales associate’s inbox. This way they don’t have to leave Outlook (the application that they live in all day) to see corresponding line-of-business (LOB) data, in this case order history, right in the email message. LOB data is exposed via a simple REST-based WCF data service (built in part 1) and is displayed in the reading pane in Outlook for each customer.

Today I want to take a look at how we can migrate the Outlook client we built in part 2 to Office 2010 and .NET Framework 4.0 Client Profile using Visual Studio 2010. The VSTO 4 Runtime has changed so there are some manual steps you may have to take when you migrate your solutions depending on what features you are using. 

I set up a Windows 7 VHD with Visual Studio, SharePoint & Office Betas that I’ll be using for development, just like I explained in this post - Setting up Windows 7 for Office & SharePoint 2010 Beta Development. If you’d like to follow along you can download the Visual Studio 2008 version of the code here.

Required Changes for the New VSTO 4 Runtime

First I want to point out some excellent information on MSDN that you should familiarize yourself with if you are migrating Office solutions built with Visual Studio (VSTO). All the samples, walkthroughs and videos on the Visual Studio 2010 resources page on the VSTO Dev Center are valuable but if you are migrating you should pay close attention to this item -

Migrating Office Solutions to the .NET Framework 4

I also recommend familiarizing yourself with the new VSTO4 Runtime here -

Visual Studio Tools for Office Runtime Overview

In particular, if your Outlook project uses Form Regions or the Ribbon designer then you will have some work to do if you target .NET Framework 4 because the VSTO 4 runtime has been updated to use Office object model interfaces and not directly inherit from classes like was done in the previous versions of the VSTO runtime. Moving forward this is a very good thing because it removes the dependency on specific versions of Office and enables Office solutions to use the new embedded interop types feature (sometimes referred to as “no-PIAs”) in the .NET Framework 4. This means solutions can run on end user computers without installing these primary interop assemblies (PIAs).

So you may have to reach into the designer generated files and tweak some code. Note that depending on how you structure your application it may be easier after you retarget to .NET 4 to just create a new Form Region and a new Ribbon and copy your user code into them. This is especially true if you are migrating solutions using Visual Studio Beta 2 to .NET 4 because of a bug which I’ll show you how to work around. Let’s take a look at what we need to do for our Northwind Outlook client.

Migrating the Outlook Solution to Visual Studio 2010

Before I jump into migrating this solution I should point out that building Office client solutions in Visual Studio became a lot easier starting in Visual Studio 2008 with all the designer support that was added directly into the Visual Studio box. You can create form regions, ribbons, and host WPF controls easily using these tools in VS2008 without having to install additional extensions. These tools have been updated in Visual Studio 2010 to allow you to build upon Office 2010 but they are just as easy to use.

So let’s start with opening the NorthwindOutlookClient solution in Visual Studio 2010. The migration wizard will open and you’ll be guided through conversion of the project to VS2010. Note that depending on what you have installed on your development machine will determine what Visual Studio does with the solution. If you have the .NET 3.5 framework installed then the target framework will not change, however if you only have .NET 4 then it will update to that automatically. (For more information see How to Upgrade Office Solutions and Configuring a Computer to Develop Office Solutions).

Since my development environment is a Windows 7 machine set up with Office 2010, Visual Studio 2010 and SharePoint Foundation 2010 I get some warnings immediately about the version of Office being wrong. “This project requires Microsoft Office Outlook 2007, but this application is not installed.” However it will build and run just fine in Outlook 2010.

image

Updating the Target Framework to .NET 4

Now let’s change the target framework to .NET Framework 4 Client Profile. In Visual Basic you do this by selecting the project properties Compile tab, scroll down to “Advanced Compile Options…” and then select the new framework in the dropdown.

image

I should mention that the Client Profile is a subset of the full .NET 4 Framework meant for client applications like WPF, Windows Forms and Office solutions.  This means it’s a smaller install if your users don’t have the .NET Framework installed at all. It doesn’t include any server pieces like ASP.NET. This is also now the default framework target for new Windows, WPF and Office projects.

So once we change the framework, this will close and then reopen the project. Now we’ve got a bunch of stuff in our errors list. Ouch. There are 48 errors but only 25 of them are related to the VSTO 4 runtime changes. The minor problem is that the WPF control is referencing System.Xaml and when we switched the target this gets dropped (at least it does in Beta 2) so we need to just reference this 4.0 assembly. If you open up the OrderHistory.g.vb file, hover over the error, and drop down the error correction it will take care of this for you.

Much more important is the 25 errors around our Form Region. We have two options, we can either add a brand new Form Region to the project, add our controls, paste our custom code into it, and delete the old one, or we can modify the designer generated code. In this case, because there is only one user control on the Form Region, it’s much easier to just create a new one. But if you have very complicated layouts you may need to take the route of updating the designer code to use the new interfaces. For the sake of learning what we need to do in these trickier situations, I’ll opt to go that route too.

Update Outlook Projects that Contain Form Regions

Since we’ll be modifying the designer generated code you’ll need to make sure you click the Show All Files toolbar button on the Solution Explorer. Now we can open up the EmailForm.Designer.vb file in the editor.

This documentation contains the step-by-step code conversions that you need to perform to upgrade your form regions. First we need to modify the declaration of the form region class so that it derives from FormRegionBase instead of FormRegionControl:

 <System.ComponentModel.ToolboxItemAttribute(False)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class EmailForm
    Inherits Microsoft.Office.Tools.Outlook. FormRegionBase

The documentation also shows what the code looks like against a 3.5 target and what it should look like against a 4.0 target. For instance we have a problem with the constructor. In 3.5 we have:

     Public Sub New(ByVal formRegion As Microsoft.Office.Interop.Outlook.FormRegion)
        MyBase.New(formRegion)
        Me.InitializeComponent()
    End Sub

However in .NET 4 we need to change this to:

     Public Sub New(ByVal formRegion As Microsoft.Office.Interop.Outlook.FormRegion)
        MyBase.New(Globals.Factory, formRegion)
        Me.InitializeComponent()
    End Sub

Next we need update the signature of the InitializeManifest method (note that underscores are not necessary here in Visual Basic when targeting .NET Famework 4)

 Private Shared Sub InitializeManifest(
     ByVal manifest As Microsoft.Office.Tools.Outlook.FormRegionManifest,
     ByVal factory As Microsoft.Office.Tools.Outlook.Factory)

A Better Way to Upgrade (for now) – Working Around a Bug in Visual Studio 2010 Beta 2

Steps 5-8 of the documentation tell us to create a new Form Region to get the new factory code that we need -- this is why it may just be easier for you to start this way. Unfortunately there’s a bug in Visual Studio 2010 Beta 2 when you attempt to add a new item to the project it will select the wrong language. If you have a C# project it will select VB templates and if you have a VB project it will select C# templates. Doh! (Now that’s taking language parity thing a bit too far ;-)) So the way you have to work around this is to create a new Outlook Add-in project based on .NET 4 already and then add a new form region there. This is a bug only with migrated projects and it will be fixed in the final release.

However, if you also want to keep building upon this solution in Beta 2, you should recreate the project, selecting a .NET 4 target, and add your existing files into it. This will ensure that things work smoothly going forward. You can actually name the project the same thing, you just need to put it into a different folder. Here’s how you do it.

First clean the solution to remove the registered Add-In . From the menu select Build –> Clean NorthwindOutlookClient.

Now since we have multiple projects in this solution you can right-click on the NorthwindOutlookClient in the Solution Explorer and then select Remove. Then close Visual Studio and open the folder where the NorthwindOutlookClient resides and rename that folder to NorthwindOutlookClient_OLD. Reopen the solution in Visual Studio.

Next go to the main menu and select File –> Add –> New Project then make sure you select .NET Framework 4 and then choose Outlook 2010 Add-in and name it NorthwindOutlookClient.

image

Next right-click on the project and select Set as StartUp Project. Then Add –> Existing Item and select all the code, app.config, .xaml and .gif files in the NorthwindOutlookClient_OLD directory:

image

When prompted, say YES to overwrite ThisAddIn but say NO to overwrite ThisAddIn.Designer file. (Even though we didn’t select the designer file, it is automatically brought in when we select ThisAddin.)

Next we need to re-add our Service Reference to our data service by right clicking and select Add –> Service Reference, click Discover button and then name the service the same thing, NorthwindService.

Next make sure the .gif file is set to a Resource in the file properties. Select the file in the Solution Explorer and in the properties window set the Build Action to Resource.

Finally, because we are using WPF controls in this solution, right-click and select Add Reference to add the .NET assembly references for System.Xaml, PresentationCore, PresentationFramework, WindowsBase and WindowsFormsIntegration.

Back to Migrating the Form Region

Now you can get back to upgrading your Form Region as explained in steps 5-8 . Add a new Form Region by selecting Project –> Add New Item and then selecting Form Region (now you’ll see the right templates). Walk through the wizard and then Show All Files again to open the designer generated file and grab the code for the two partial classes (note that the factory is an embedded class):

image

It’s not too painful to change your original code once you get the right template for it. You just need to make sure that after you paste in these two partial classes, you update all instances of the class name with yours. In our case the name of the Form Region is EmailForm so the updated code back in the NorthwindOutlookClient project will look like this (I know it’s ugly that’s why it’s designer generated ;-). As an aside, look at where underscores are still necessary to resolve ambiguity if there is a line break):

 Partial Public Class EmailFormFactory
    Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory

    Public Event FormRegionInitializing As  _
        Microsoft.Office.Tools.Outlook.FormRegionInitializingEventHandler

    Private _Manifest As Microsoft.Office.Tools.Outlook.FormRegionManifest

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
    Public Sub New()
        Me._Manifest = Globals.Factory.CreateFormRegionManifest()
        EmailForm.InitializeManifest(Me._Manifest, Globals.Factory)
    End Sub

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
    ReadOnly Property Manifest() As Microsoft.Office.Tools.Outlook.FormRegionManifest _
             Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory.Manifest
        Get
            Return Me._Manifest
        End Get
    End Property

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
   Function CreateFormRegion(ByVal formRegion As Microsoft.Office.Interop.Outlook.FormRegion) As _
             Microsoft.Office.Tools.Outlook.IFormRegion _
             Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory.CreateFormRegion
         Dim form As EmailForm = New EmailForm(formRegion)
        form.Factory = Me
        Return form
    End Function

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
    Function GetFormRegionStorage(ByVal outlookItem As Object,
                  ByVal formRegionMode As Microsoft.Office.Interop.Outlook.OlFormRegionMode,
                  ByVal formRegionSize As Microsoft.Office.Interop.Outlook.OlFormRegionSize) _
             As Byte() _
             Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory.GetFormRegionStorage

        Throw New System.NotSupportedException()
    End Function

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
    Function IsDisplayedForItem(ByVal outlookItem As Object,
                ByVal formRegionMode As Microsoft.Office.Interop.Outlook.OlFormRegionMode,
                ByVal formRegionSize As Microsoft.Office.Interop.Outlook.OlFormRegionSize) _
            As Boolean _
            Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory.IsDisplayedForItem

        Dim cancelArgs As Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs =
            Globals.Factory.CreateFormRegionInitializingEventArgs(outlookItem,
                                                                  formRegionMode,
                                                                  formRegionSize,
                                                                  False)
        cancelArgs.Cancel = False
        RaiseEvent FormRegionInitializing(Me, cancelArgs)
        Return Not cancelArgs.Cancel
    End Function

    <System.Diagnostics.DebuggerNonUserCodeAttribute()> _
    ReadOnly Property Kind() As Microsoft.Office.Tools.Outlook.FormRegionKindConstants _
             Implements Microsoft.Office.Tools.Outlook.IFormRegionFactory.Kind
        Get
            Return Microsoft.Office.Tools.Outlook.FormRegionKindConstants.WindowsForms
        End Get
    End Property
End Class
End Class

Partial Class WindowFormRegionCollection

    Friend ReadOnly Property EmailForm() As EmailForm
        Get
            For Each Item As Object In Me
                If (TypeOf (Item) Is EmailForm) Then
                    Return CType(Item, NorthwindOutlookClient.EmailForm)
                End If
            Next
            Return Nothing
        End Get
    End Property
End Class

Now it looks like all our project errors have disappeared (thanks to Visual Basic’s background compiler). However if you didn’t create a brand new project like I explained above in order to work around that migration bug, then when you rebuild the solution you’ll see the build still fails. In this case we have one more thing to do.

Removing the SecurityTransparent Attribute

The next section of the documentation on upgrading Office solutions has information on how to Remove the SecurityTransparent attribute from Office Projects that you upgrade from Visual Studio 2008. Under My Project node in the Solution Explorer, open up the AssemblyInfo file and you’ll see at the bottom the SecurityTransparent attribute. Remove that line of code. If you created a new project based on .NET 4 from the get-go this file will already be correct.

Run it!

Now you should be able to rebuild the project and it will succeed. Hit F5 and now we have the Outlook client working and looking as before, but now we are targeting the .NET framework 4 and we have all the new features at our disposal.

As you can see there are some manual steps to migrating Office solutions to the .NET Framework 4, but once you are familiar with the changes, it’s pretty straightforward. Keep in mind that if you are migrating your own solutions and you have code that loads form regions and/or ribbons dynamically at runtime you will also have to update that code as well to use the factory methods and interfaces instead of classes, take a look at the documentation for details. Hopefully dealing with a little migration pain now will pay off in the long run to you and your users.

Resources

For more information on building Office solutions with Visual Studio please check out:

For more information on underscores in Visual Basic 10 in Visual Studio 2010 see:

Enjoy!