.Net Framework 4, NoPIA and Add-in Express 2010
Let's take a break from the Northwind Traders application for a while and talk about a new feature available in .Net Framework 4 : NoPIA or Type Embedding.
As you all know when we developed Office Add-ins or applications that integrated with the Office suite of products we have to include a reference to the Office PIAs or Primary Interop Assemblies; these PIAs is the connection point between our .Net application and the Office COM server. Generally, deploying the Office PIAs presented its own share of challenges and problems. It is a pretty big installation/download which is understandable as you get all the Office functionality in the dll’s. This included all the structs, enum, COM Interfaces etc.
Ok, so what exactly does NoPia mean to you as an Add-in developer? Well, for one, you do not need to deploy the Office Primary Interop Assemblies to the users machines anymore and when embedding the Interop Type, the compiler will be smart enough to only embed the types you have actually used! Best way to explain this is to show you.
For illustration, we’ll be using Red Gate’s .NET Reflector to show you how assembly looks like before we embed the interop types. When opening your assembly, expand the list of references:
Notice the Interop.Word reference. Now, let’s embed the Interop.Word types into our own assembly. In order to do this, select it in your projects’ list of references. In the Properties window, set the Embed Interop Types property to True.
Compile your project and refresh it in Red Gate’s .NET Reflector. Notice the Interop.Word assembly had been removed and your assembly now has a new Word namespace. Expand this namespace and you will only see the object interface you have actually used in your code! In this scenario I’ve only opened a MS Word document and the only object I’ve used was Word.Document:
Just to ensure that this is actually true, declare a new paragraph in your Add-in code:
Word.Paragraph newParagraph = wordDoc.Paragraphs.Add();
Recompile you project and refresh .NET Reflector and expand the Word namespace. Did you notice the Paragraph and Paragraphs interface?
The compiler included it, because you used it in your code. Pretty nifty, isn’t it?
Type Equivalence
Personally I prefer using C# as language of choice, however, when it came to MS Office add-ins and integration I always opted for using VB.Net. This was due to the fact that VB is a lot more “forgiving” when using the MS Office APIs. For example, the following code opens a MS Word file using VB.Net:
Dim WordApp As Word.Application = New Word.Application() Dim WordDoc As Word.Document = WordApp.Documents.Open("C:\Temp\WordDocument.doc") WordDoc.Activate() WordApp.Visible = True
To compile the above, I needed to add a reference to the Microsoft.Office.Interop.Word.dll and I’ve added the following to the top of my class to make the code a bit more readible:
Imports Word = Microsoft.Office.Interop.Word
To get the same done in C# takes a bit more time … here is the C# code to accomplish what we’ve done above:
object missingObject = System.Reflection.Missing.Value; object isReadOnly = false; object isVisible = true; object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"C:\temp\WordDocument.doc"); Word.Application wordApp = new Word.Application(); Word.Document wordDoc = wordApp.Documents.Open(ref fileName, ref missingObject, ref isReadOnly, ref missingObject, ref missingObject, ref missingObject, ref missingObject, ref missingObject,ref missingObject, ref missingObject, ref missingObject, ref isVisible, ref missingObject, ref missingObject, ref missingObject, ref missingObject); wordDoc.Activate(); wordApp.Visible = true;
Once again for code readability, I’ve added the following to the top of my class:
using Word = Microsoft.Office.Interop.Word;
Ok, so I guess it is pretty obvious at this point why I prefer using VB.Net when doing Office add-ins. It’s just messy and a nightmare to read, forget about maintenance. Luckily for us in C# 4, things has gotten considerabily easier thanks to .NET 4’s Type Equivalence. Here is the C# 4 code to accomplish the same:
string fileName = @"C:\temp\WordDocument.doc"; Word.Application wordApp = new Word.Application(); Word.Document wordDoc = wordApp.Documents.Open(fileName, ReadOnly: true, Visible: true); wordApp.Visible = true;
This is pretty good news for C# developers as it is considerably easier to develop Office Add-in using .NET 4. Also bear in mind that this is not just applicable to MS Office but any COM server.
Until next time, keep coding!
8 Comments
Thanks for the post. Lots of good info. Some follow-up questions:
– Is NoPIA supported by ADX add-ins for Outlook 2003 and 2007, or is this specific for Outlook 2010? In other words, as long as the machine has .Net 4 installed an add-in for Outlook 2003/2007 will support this without issues?
– What is the version of the OOM interface available? Or is it that if in my project I use, say Outlook 2003’s PIA, that will be the OOM interfaces available?
– Related to the previous question, if I use Outlook 2003 PIAs as the basis of my add-in and then Reflection to use some specific feature of Outlook 2007/2010 (checking for the version of the host app), will this NoPIA technology work in this case?
– Is this available in both the full .Net 4 framework and the new .Net 4 Client Profile?
Hi Esteban,
I’ve answered your questions below:
Q: Is NoPIA supported by ADX add-ins for Outlook 2003 and 2007, or is this specific for Outlook 2010? In other words, as long as the machine has .Net 4 installed an add-in for Outlook 2003/2007 will support this without issues?
A: In short, Yes. NoPIA will work on any COM server, thus Office 2003, 2007 and 2010.
Q: What is the version of the OOM interface available? Or is it that if in my project I use, say Outlook 2003’s
PIA, that will be the OOM interfaces available?
A: Haven’t played around with different versions yet, but I would assume that yes, if you added the Office 2003 PIA’s only the interfaces for that version would be available.
Q: Related to the previous question, if I use Outlook 2003 PIAs as the basis of my add-in and then Reflection to use some specific feature of Outlook 2007/2010 (checking for the version of the host app), will this NoPIA
technology work in this case?
A: Mmmm…interesting question. Don’t think NoPIA would be of much help, as reflection you use/load the entire dll directly and don’t embed the type into your assembly.
Q: Is this available in both the full .Net 4 framework and the new .Net 4 Client Profile?
A: It is. The Client Profile is a subset of the full .NET framework that includes only the assemblies needed for
desktop apps. You can see a list of features that’s available for the Client Profile here:
Hope this answered your questions. Thanks for your comment.
Kind Regards,
Pieter
Hello Esteban,
My 2 cents. :)
Q: Related to the previous question, if I use Outlook 2003 PIAs as the basis of my add-in and then Reflection to use some specific feature of Outlook 2007/2010 (checking for the version of the host app), will this NoPIA technology work in this case?
A: I agree with Pieter, this question is interesting. But I believe NoPIA and Reflection are a bit different things in this case. NoPIA embeds all explicitly used types (interfaces, enums, etc.) into the assembly, while Reflection works with methods and properties directly. I have just tested the code below (modification of Pieter’s sample), the code works and there are no new embedded types in the assembly:
If you have other questions don’t hesitate to post.
Hi, thank you both for the comments.
Pieter/Dmitry, two related questions:
– Have you tried actually deploying a .Net4 / NoPIA add-in for Outlook using ClickOnce? In your example where you compile your sample project a couple of times (where different libraries get automatically linked / referred), if you were to deploy the project would you expect to see different DLLs sent to the client each time?
– And a somehow related question about .Net frameworks (now that you are making me consider it). Do you know if it’s possible to “upgrade” the .Net framework of an already deployed add-in? For example, consider you already have deployed and add-in targeting .Net 2.0 using ClickOnce. Is it possible to compile a new version of and existing add-in now targeting .net 4 and make it available as an “update” (CheckForUpdates) to the existing app or users will have to uninstall that application and install it again from a newer location (.net 4)?
Thanks for all your comments.
Hello Esteban,
Q: Have you tried actually deploying a .Net4 / NoPIA add-in for Outlook using ClickOnce? In your example where you compile your sample project a couple of times (where different libraries get automatically linked / referred), if you were to deploy the project would you expect to see different DLLs sent to the client each time?
A: I have just tried to deploy a .NET4 add-in with Embed Interop Types property set to true, it worked fine, updates also work as expected. I am a bit confused by the second part of your question. You can build your project as many times as needed but your users will not get any updates unless you publish it.
Q: And a somehow related question about .Net frameworks (now that you are making me consider it). Do you know if it’s possible to “upgrade” the .Net framework of an already deployed add-in? For example, consider you already have deployed and add-in targeting .Net 2.0 using ClickOnce. Is it possible to compile a new version of and existing add-in now targeting .net 4 and make it available as an “update” (CheckForUpdates) to the existing app or users will have to uninstall that application and install it again from a newer location (.net 4)?
A: Good question! I am going to test this scenario and will give you the precise answer after that.
Hi Pieter,
For the first question, what I meant to say is to deploy the updated project adding one more dynamic reference. If you were to do that, would you expect to see different DLLs at the client computer? By different I mean the framework libraries deployed along your own add-in files, which of course are different after recompiling. You said that it works, which is good news, but do you see a difference in deployment size each time (maybe looking at the Publish folder of your solution?
Looking forward to your results with the “upgrade” scenario.
Thanks again!
Hello Esteban,
I finally have test results. In brief, it is impossible to install/upgrade the .NET Framework during a usual ClickOnce application update process, i.e. by using the CheckForUpdates() method.
Now details:
– I created a simple COM add-in for Microsoft Outlook in Visual Studio 2005. I used version-neutral PIAs, target framework was, naturally, 2.0. The add-in has just one Explorer command bar with one button – Check For Updates that calls the CheckForUpdates() method;
– I published the add-in (version 1.0.0.0, Prerequisites are .NET Framework 2.0 and Windows Installer 3.1) and installed it on a test PC by using the setup.exe bootstrapper. The test PC was Windows 7 with Office 2007 and .NET Framework 2.0 already installed;
– Then I made two updates (1.1.0.0 and 1.2.0.0), published both and installed both one by one via clicking the button in Outlook Explorer;
– I opened the solution in Visual Studio 2010, changed the target framework to 4.0, set the Embed Interop Types property to True for three interop assemblies (Interop.Office.dll, Interop.Outlook.dll and Interop.VBIDE.dll), cleaned the output directory and rebuilt the add-in;
– I published the add-in (version 1.5.0.0, Prerequisites are Microsoft .NET Framework 4 Client Profile);
– After calling the CheckForUpdates() method, ClickOnce detected the new update and suggested installing it but failed to install the update with a message like “Microsoft .NET Framework 4 should be installed. Please contact your administrator.”
Good news is that after installing .NET Framework 4.0, the CheckForUpdates() method worked correctly and installed version 1.5 of the add-in. In fact this is relatively good news, because the main question was “is it possible to ‘upgrade’ the .Net framework of an already deployed add-in?”, the answer is – no, it is not possible. This is how things stand at the moment…
Hi Esteban,
Another interesting question. I did a quick experiment, where in one scenario I declare a paragraph and another where i remove the declaration. In the first instance the built dll is 8K and the second it’s 7K. So yes, it does have an impact on the size, as it embeds the necessary types.
Kind Regards,
Pieter