Can you build one add-in for multiple versions of Office?

This of course is the advantage of using the old "shared add-in" project types – you can build one add-in that targets all versions of all Office apps that support COM add-ins (ie, 2000 onwards). The question is, can you do something similar with VSTO add-ins? The answer is "No, not in any supported way".

If you're a responsible upright citizen, you'll stop reading now.

So, what kind of a cowboy hacker are you anyway? OK then, we'll explore the reasons why it's hard to get this to work, and hopefully it will become obvious why this is not supported – and also why you don't really want to do this.

First, VSTO add-ins only support Office 2003 and later (so, right now, that's Office 2003 and 2007), and we don't support development on a machine with multiple versions of Office. We also don't support mixing PIA versions and Office versions in the same project. This is problematic, especially for ISVs. Technically, it is actually feasible to build a single VSTO add-in that targets both 2003 and 2007, but if you do so you're off into unsupported territory.

If you want to go ahead anyway (and let me say again: this is not supported), there are some useful techniques you can adopt – and some of these are useful even if you do stay with a supported model. First, you need to target the lowest common denominator (Office 2003 in this case), and then write conditional code to use version-specific functionality dependent on which version you're actually running in. While you're doing this, it makes sense to abstract out the version-specific pieces to separate classes, or even to separate DLLs. Separate DLLs are particularly useful – because then, you could reuse these in multiple add-ins – either version-resilient add-ins or version-specific add-ins. You can build each version-specific DLL on a machine with that version of Office and the Office PIAs.

Here's an example.

· (On a machine with Office 2003) I build a DLL called Ui2003, with a set of CommandBar code.

· Then (on a machine with Office 2007), I build a DLL called Ui2007, with a set of Ribbon and Task Pane code.

· Then I build a VSTO Office 2003 Add-in.

· Add references to both the Ui2003 and Ui2007 DLLs.

· Write conditional code that detects the version of the host, and uses either the Ui2003 or Ui2007 functionality.

Of course, one disadvantage of this approach is that your class library DLLs cannot use any of the VSTO wrapper classes – you can only use them in a VSTO project. That means, for one thing, that your Ui2007.dll will have to use the low-level plumbing for customizing the Ribbon, Task Pane and Form Region, instead of the high-level wrappers (and design-time support) provided for VSTO Office 2007 add-ins.

Still reading?

OK, now how do you write the conditional code? The simplest possible approach is to just put a version test in ThisAddIn_Startup, for example:

private void ThisAddIn_Startup(object sender, System.EventArgs e)

{

    if (this.Application.Version == "11.0")

    {

        // 2003-specific code.

    }

    else

    {

        // 2007-specific code.

    }

}

That would work for some simple cases, but don't forget that Office 2007 introduced a new model for extensibility interfaces, where it queries for specific interfaces implemented in the add-in. This means that ThisAddIn_Startup is not the only entrypoint for Office into your code. In VSTO, the new interfaces are supported through the low-level RequestService mechanism. So if you want to support any of these interfaces, your code would have to look something like this:

private void ThisAddIn_Startup(object sender, System.EventArgs e)

{

    if (this.Application.Version == "11.0")

    {

        // 2003-specific code.

    }

}

// 2007-specific code.

protected override object RequestService(Guid serviceGuid)

{

    if (serviceGuid == typeof(Office.IRibbonExtensibility).GUID)

    {

        if (ribbonX == null)

        {

            ribbonX = new RibbonX();

        }

        return ribbonX;

    }

    return base.RequestService(serviceGuid);

}

The RequestService method will only be called if you're running in Office 2007, so it doesn't need to be in a conditional block – but it does begin to make the source code a bit messy. A cleaner approach would be to use partial classes. Using this approach, you'd keep the part of the ThisAddIn class that the project wizard provided fairly uncluttered, and abstract everything (or almost everything) version-specific to 2 new parts of the same partial class. For instance, you'd put something like this in ThisAddIn.cs:

public partial class ThisAddIn

{

    private void ThisAddIn_Startup(object sender, System.EventArgs e)

    {

        if (Application.Version == "11.0")

        {

            Initialize2003Ui();

        }

    }

}

Then, extend the ThisAddIn partial class, with another part, perhaps in a separate file called say ThisAddIn2003.cs. This will use the 2003-specific functionality you abstracted out to Ui2003.dll:

public partial class ThisAddIn

{

    // 2003-specific fields, using Ui2003.dll.

    private Ui2003.CommandBarX commandBarX;

    private void Initialize2003Ui()

    {

        commandBarX = new Ui2003.CommandBarX();

    }

}

…and in ThisAddIn2007.cs, put the 2007-specific pieces of the partial class, using the Ui2007.dll:

public partial class ThisAddIn

{

    // 2007-specific fields, using Ui2007.dll.

    private Ui2007.RibbonX ribbonX;

    protected override object RequestService(Guid serviceGuid)

    {

    if (serviceGuid == typeof(Office.IRibbonExtensibility).GUID)

    {

    if (ribbonX == null)

        {

            ribbonX = new Ui2007.RibbonX();

    }

    return ribbonX;

    }

    return base.RequestService(serviceGuid);

    }

}

Now, when you add a reference to Ui2003.dll to your Office 2003 project, everything is hunky dory, and still within supported territory. However, you will hit a problem when you add a reference to Ui2007.dll. Specifically, the compiler will complain that there are conflicting versions of the core Office PIA in the project, one referenced by the add-in (2003) and one by Ui2007.dll (2007). The only way to fix this is to change the reference in the add-in from the 2003 PIA to the 2007 PIA – and now you're off in unsupported territory.

Also, how does this reconcile with what I said in my previous post, Why is VS development not supported with multiple versions of Office? Office does support running multiple versions of Office on the same machine, although it is discouraged. What's not supported is developing with VS on a machine with multiple versions of Office. Also, as specified in KB 840585 and KB 928091, "The Office 2003 managed code add-ins must be built against the Office 2003 PIAs. The Office 2007 managed code add-ins must be built against the Office 2007 PIAs. Therefore, if you build an add-in solution that you intend to use with several versions of Office, Microsoft recommends that you build a version of your add-in for each version of Office that you intend to support."

The technique I'm describing here does not require multiple versions of Office on the machine. Indeed, it doesn't require any version of Office. It also doesn't necessarily require multiple versions of the PIAs, so long as you build your 2003-specific DLL on a machine with the 2003 PIAs, and you build your 2007-specific DLL (and the ultimate add-in that consumes this DLL) on a machine with the 2007 PIAs. However, it does require you to build a 2003 add-in project which references the 2007 PIAs, and that's not supported .

Even if you can get the thing built on your imaginary dev box, what happens when you want to test it and when you want to deploy it? If you test/deploy on a machine with only Office 2007 and the 2007 PIAs, all is goodness – your 2003 add-in runs with the 2007 PIAs in Office 2007. But, what happens when you test/deploy on a machine with only Office 2003 and the 2003 PIAs? The add-in will fail to load because it's bound to the 2007 PIAs which are not found. OK, so what if you deploy a private copy of the 2007 PIAs? Well, first, you'd have to bind to this private copy at compile time in the first place. Second, it won't help you anyway, because fusion will look in the GAC first – where it will find the 2003 PIAs. So, your 2003 add-in will only work if your 2003 machine has the 2007 PIAs installed in the GAC.

There's another point to consider: 2003 add-ins will be registered in the way Office 2003 expects, and therefore will use the VSTO v2 runtime – even when running in Office 2007. Office 2003 doesn't know any other way to load add-ins. 2007 add-ins, on the other hand, will be registered to take advantage of Office 2007's faster load path, and the enhancements to ClickOnce, and will use the VSTO v3 runtime. The VSTO v3 runtime uses full ClickOnce and streamlined security that does not depend on the version-specific CAS repository. 2003 add-ins must use the VSTO v2 runtime, which uses an emulated ClickOnce, purely CAS-based security, and is dependent on the version-specific CAS repository. Plus, the VSTO v3 runtime is the first version-resilient version – which means your 2007 add-ins are designed to work with the v3 and later runtimes – whereas 2003 add-ins will always require the v2 runtime.

So, do you want to keep building 2003 add-ins, even though you're targeting Office 2007? It seems to me, you really want to move on. Office has moved on – 2007 introduced major changes from previous versions. VSTO has also moved on. Surely, you'd want to take advantage of the new features and enhancements, and build add-ins that specifically target the later version. If you also need to target Office 2003, perhaps you should just bite the bullet – that is, accept that you're going to have a 2003-specific add-in and a separate 2007-specific add-in, instead of trying to munge them together in some way that cannot by definition be optimal for either version. My advice is to build 2 separate add-ins, one for 2003 (which will continue to run in 2007 and later), and one for 2007 (and later).

If you do choose the approved path, and build 2 separate add-ins, there are simple ways to optimize your re-use – more on this later.