When migrating VSTO solutions targeting .NET 3.5 to .NET 4.0 people are noticing some design changes. Our documentation on migration puts it pretty straightforward about this and does not explain the underlying reasoning for these changes:
“If the target framework of an Office project is changed to the .NET Framework 4 from an earlier version of the .NET Framework, some additional steps might be required to continue to run the solution on development and end user computers”
I though I will re-use the old email thread on the issue and hopefully some people can find this information useful. Below is the slightly edited copy&paste of the email. It contains some abbreviations that might not be obvious so here is rough translation table:
M.O.T = Microsoft.Office.Tools
M.VS.T.A. = Microsoft.VisualStudio.Tools.Application
NoPIA = CLR 4.0 functionality for Type Equivalence and Type Embedding. It can be activated in Visual Studio 2010 by setting “Embed Interop Types” property on the assembly referenes to ‘True’
From: Misha Shneerson
Subject: RE: Bug Discussion on VSTO Technology
> Since then though my solution is looking to distribute the new utilities.v4 assemblies and causing a distribution issue at the moment, could you elaborate a little on this so can help me understand.
This is actually by design. Let me try and explain the overall picture and where the utilities assemblies fit in.
All our new core assemblies with public APIs – i.e. M.O.T.Common, M.O.T.Excel etc (well, that’s all except M.VS.T.A.Runtime.dll) do contain interfaces. This is instead of classes that the user code derived from in the previous version.
So, for example, Microsoft.Office.Tools.Excel.Workbook is now an interface.
At this point you should be going like “Huh! Why in the world would they do that?”.
In CLR4 there is a new feature available which is codenamed NoPIA (I hope you did hear about this – if not there are couple of posts on my blog on the topic) where the original idea is that compilers will embed PIA interfaces (thus creating local copy of the interface in the assembly itself) and CLR will consider multiple copies of the same interface to be equivalent. Basically, that means that if assembly A has a method Foo(M.O.I.Excel.Application xlapp) and assembly B wants to call Foo(xlapp) – each one will operate on its own copy of the M.O.I.Excel.Application interface but the code will still work. And all this w/o even loading Microsoft.Office.Interop.Excel.dll into memory!
So, we have applied same NoPIA principles to VSTO Object Model as well – VSTO’s core OM now consists of interfaces which do qualify for type equivalence.
So, why we are doing all this? This is actually to allow VSTO runtime and VSTO code to version independently of each other. I.e. if the user code does take dependency on VSTO’s runtime assembly – and then we rev up our runtime (and all assemblies that come with it) – we still want to load the same code w/o recompilation. Using interfaces and type equivalence allow us to do so.
Now, everything is good – so why all these Utilities assemblies and what are they for?
In the past user’s ThisWorkbook derived from VSTO’s Microsoft.Office.Tools.Excel.Workbook class. This did allow for some very nice syntax where developers could write their code using familiar VBA notation e.g. Me.ActiveSheet.
But the problem with making Microsoft.Office.Tools.Excel.Workbook an interface is that now user code cannot derive from this interface. In order to maintain the nice old Me.XXX syntax we needed user’s ThisWorkbook to derive from another class which just redirects all its calls to Microsoft.Office.Tools.Excel.Workbook interface. Now, welcome to Microsoft.Office.Tools.Excel.WorkbookBase class in the Utilities assembly! That’s exactly what it is doing – just redirecting calls to M.O.T.Workook interface methods. Actually, if you look into the Utilities assembly you will see that all of these are just thin calls through on methods from one or another interface.
The bad part is that the versioning of classes is different than versioning of interfaces. But since user code takes dependency on these Utilities assemblies – those assemblies and user assemblies are now tightly coupled together and need to be deployed together (e.g. if VSTO runtime would rev up those assemblies and user code tried to bind to the rev’d up assemblies – it would fail). From the versioning perspective user code and utilities assemblies are now one coherent unit. This coherent unit talks to VSTO runtime classes through interfaces.
We feel safe for the developers to deploy these assemblies alongside with their code since we never intend to services these (these are just thin wrappers w/o state/code or anything that would require servicing).
What is the guidance around the core VSTO OM interfaces? How they version and can developers implement those interfaces.
Typical question I expect to hear is “Since M.O.T.Excel.Workbook is an interface – can developer just implement this interface to provide custom behaviour for some of Workbook methods?”. The short answer is – NO, do not implement these interfaces.
The long answer is – VSTO reserves the right to version these interfaces in the future versions. I.e. we want to be able to add new methods to these interfaces (similarly to what Office does with its interfaces). If we add new methods that user code does not implement – something will be broken there, either compile time or runtime or, most probably, both.
So, in our documentation we will specify which interfaces are expected to be implemented by the user. The rule of thumb is – if interface’s name starts with ‘I’ e.g. IExtension, IWorkbookExtension, ISmartTagExtension – the interface can be implemented by 3rd parties – it is locked down and we guarantee to not modify it in next version.
Hope this helps to clarify the picture,
To: Misha Shneerson; Rachel Falzone Schaw
Subject: RE: Bug Discussion on VSTO Technology
I have managed to get a little further, one of the developers had changed the Codebehind Partial class and this rename had knocked out the build and then it was me trying to reference old assemblies that caused the problem. I have since created a fresh project and noticed that the utilities class had the functionality and it was looking for a needle in a haystack because was a simple case naming error in my class L.. Since then though my solution is looking to distribute the new utilities.v4 assemblies and causing a distribution issue at the moment, could you elaborate a little on this so can help me understand.
Thanks for your response.