Supporting several Office versions in an add-in. Interop assemblies and late binding.
- What interop assembly to choose for your add-in project?
- How does an interop assembly version influence the development time?
- How to support a given Office version correctly?
When accessing a COM type library in .NET via early binding you need to use a corresponding interop. An interop for an Office application is a .NET assembly providing meta-information about objects, properties, methods, and parameters available in the type library (=object model) of the Office application. Microsoft provides interops for all Office applications starting from Office version 2002. These interops are called Primary Interop Assemblies or PIAs. Add-in Express for Office and .NET provides version-neutral interops that are interops for Office 2000 applications.
When developing an Office add-in that supports only one Office version, an easy and straightforward way is to choose the interop for that Office version. But see NOTA BENE at the end of this post.
When developing an Office COM add-in supporting several versions of an Office application, the typical approach is to use the interop for the oldest Office version supported by your add-in. Because versions of an Office application are almost 100% backward compatible, the oldest application version supported by your add-in is often thought of as the least common multiple.
A typical problem with this approach is demonstrated by the figure below:
For instance, the situation above occurs when you use an Outlook 2003 interop and you need to program features introduced in Outlook 2007 or Outlook 2010. It’s quite natural, however, as the Outlook 2003 interop doesn’t contain early-binding information for things introduced later, in Outlook 2007 or Outlook 2010.
In such a situation you need to use late binding. This approach is illustrated in the figure below:
In .NET, you use the System.Type.InvokeMember method to access a method via late binding. There’s a lot of code samples demonstrating the use of this method on our forums. For instance, see Get Outlook profile name programatically.
There are several issues with late binding calls. First off, because the compiler doesn't have information about the method (property) called, it doesn’t help you costruct such a call. Therefore it takes more time to write it. In addition, the late-binding call requires more time to debug.
There is an approach that lets you diminish that time: you can start developing your add-in using interops for the highest version of the Office application. When this is done, you can replace early-binding calls to the features introduced in this Office version with late binding calls and make sure these calls work. Then you can replace the interop assemblies referenced by your project with the interop for a previous version of the application and reiterate the procedure. Kind of refactoring. Whether this approach is useful or not, this is you who decides.
On a rare occasion, you may run into different behavior demonstrated by the object model of the host application for early and late binding calls. Say, calling Outlook.ActiveWindow via early binding led to a problem with entering VBA code in the VBA IDE of Outlook 2003, late binding did not raise such a problem. Don’t know if similar problem exist for late binding calls.
Another problem with late binding calls is that they are relatively slow. More specifically, a late binding call might be many times slower than the early binding call. This is because the method you are calling to must be found in the type library first.
In a typical add-in, this problem is not critical, and does not matter much. It does matter however if you do a late-binding call many times, say, in a nested loop. Sometimes there are ways to speed up your code by using other algorithms. Say, instead of getting/setting the values of many Excel cells one-by-one, you can get/set an array of values by calling Range.Value for a Range object covering the cells. In Outlook, you can use Extended MAPI to scan MailItems and their attachments in a given folder. If it isn’t possible to modify the algorithm, the workaround is to use the interop assembly providing early binding information for such a call.
But this workaround produces another problem shown in the figure below:
In that situation, you can check the ADXAddin.HostVersion property before calling such a method.
NOTA BENE. A variation of this scenario occurs when your add-in supporting, say, Outlook 2007 and higher is loaded by Outlook 2000, 2002, or 2003. Indeed, if all files are supplied with your setup project (setup projects generated by Add-in Express include interop assemblies by default), then your add-in will be loaded by an unwanted Office application version and if no measures are taken, there’s a high probability that the code will call a method missing in the current type library, which naturally result in the add-in crash.
So, the conclusion: if you don’t want your add-in to crash when loaded by an “unsupported” Office version, you need to check ADXAddinModule.HostVersion and disable the UI and event handling of your add-in if the add-in is loaded by such an Office version.