One of the features I was looking forward to the most with .NET 4.0 was the ability to embed interop types in managed assemblies. Interop assemblies can quickly and easily become quite painful to work with, during both the development and deployment phases of a product.
What does this new feature mean for those of us who design software that has to deal with Office (sometimes against its own will, I’ll admit)? No more PIA’s (Primary Interop Assemblies)! This is great, as PIA’s are a horrendous source of failure for much of the software out there (mainly in regards to the deployment of said software).
I strongly believe that, unless you’ve developed a full-blown add-on to an Office product, you have failed if you find yourself dependent on the PIA’s! You can create Excel spreadsheets without any help from Excel, etc. If you require Office automation to perform tasks such as readable-by-Office document creation, you have failed! What you are doing is barbaric and a purely crude act of communication between two separate processes comparable to human communication via cave paintings or petroglyphs.
Well…now, for the most part, even if you are developing a full blown add-on to Office, you can skip over most of the pain normally experienced when having to deal with these nightmares.
Hold on though, don’t get too excited; nothing in life is this easy. If you’re an apt individual who tends not to trip himself during the day-to-day maneuverings of life, you would have already asked yourself, “OK Microsoft, real funny, but what’s the catch?”
Well, the catch is that Embed interop types is actually only going to embed “bits” of those interop types…specifically the bits you need.
Which, in this case, means that your use of the COM objects at play here will need to be restricted to interfaces only. Yes, any code you currently have that makes use of the actual classes will cause a compilation error:
Interop type ‘Microsoft.Office.Interop….’ cannot be embedded. Use the applicable interface instead.
Are you angry? Do you throw your hands up in the air, shouting “WHY MICROSOFT!? WHY!?” with futile fervor? We’ve all been there, friend. The reason why you can only use interfaces has to do with “servicing“. Specifically: it is safe to embed metadata but not anything that may potentially contain executable code.
This restriction reared its ugly head for me while I was working on a “facade” library for Outlook at my job, which is basically our own VSTO. VSTO is another horrendous source of failure for much of the Office-related software out there these days. Not only that, but if your target audience is of the large corporate variety, VSTO is an unacceptable solution. I’ve written about that before, however, so we’ll get off that topic.
Anywhoo, there are many places in your code where you may have explicitly used COM classes instead of the interfaces; usually, this is not done because it moves us a certain way, but rather because it was thought to have been necessary at the time. One example is the Inspector interface. Inspectors can be “activated”; hell, they have an Activate() method. Naturally, we might be very interested in knowing when an Inspector is activated. If we look at the Inspector interface, however, we notice that there is no Activate event.
If you take a look at an implementation of the Inspector interface, InspectorClass, you will see just the event we are looking for: InspectorEvents_Event_10_Activate. Probably one of the worst named events I have ever seen, however, for the benefit of Microsoft, let’s just assumed this is due to a matter of process in the creation of the Primary Interop Assemblies.
Upon making this discovery, you may be feeling very proud of yourself; however, making use of the class will cause utter failure during compilation if you have Embed interop types set to true. So, you have two options here: 1) Set Embed interop types to false, or 2) do something other than going the easiest and laziest route.
I’m not sure about you, but let’s go with #2. If you browse the PIA’s a bit, you’ll see that there are some interesting looking interfaces named InspectorEvents and InspectorEvents_10. Upon closer inspection, you’ll notice that these are simply interfaces containing methods related to the events, not the events themselves.
What to do? Well, we’re almost there. There are actually two interfaces that will give us what we want, namely all of the events that can be fired by Inspectors: InspectorEvents_Event and InspectorEvents_10_Event. Don’t feel bad if you didn’t get this one: these interfaces are not going to show up through Intellisense and will be invisible in the Object Browser. Conspiracy theories aside, you would change your code from:
private Inspector _inspector; . . . ((InspectorClass)_inspector).InspectorEvents_Event_Activate += InspectorActivated;
private Inspector _inspector; . . . ((InspectorEvents_10_Event)_inspector).Activate += InspectorActivated;
…and you will be able to compile with Embed interop types enabled. Note that I used the “_10_” variation, mainly because Microsoft told me to. I do not know offhand what exactly the difference is between the “_10_” and non-”_10_” variations, but presumably use of the non-”_10_” variation will cause failure with newer Office versions, while the “_10_” variation should support all versions.
Also, if you look at the definition for the Inspector interface, you will notice that the Inspector interface implements the InspectorEvents_10_Event interface, so the cast (Inspector to InspectorEvents_10_Event) will be a safe one. It may come as a surprise to you that the Inspector interface implements InspectorEvents_10_Event. Indeed, why can’t we access the Activate event from the Inspector instance? The reason why we cannot is because the InspectorEvents_10_Event interface is defined with a ComVisibleAttribute set to false, thus hiding its members. Don’t look at me!
Note that there is an “Events_10_Event”-type interface for all of the COM object types at play here, such as ExplorerEvents_10_Event, et. al.