Back To Basics: COM Interop and No-PIA

December 28, 2013

tags: , ,
2 comments

Almost every week, I meet developers who hate COM. Some hate it with the heat of a million suns. Some have never used it, but heard horror stories from grey-haired colleagues. Other survivors still remember their experience fighting ATL linker errors in Visual C++ 6.0. And you know what, you can hate COM all you want. But it’s a little like hating electricity. Or the Internet. Because — on Windows — COM powers almost everything. Office. The Windows Shell. This beautiful cozy Windows Runtime APIs that you’re enjoying from JavaScript and C#. And did you know that the .NET CLR itself exposes a bunch of useful COM objects?

I guess what I’m saying is that you can hate COM, but you have to understand it, at least on a basic level. And because COM is ubiquitous, accessing COM objects from .NET is still important and useful. That’s the purpose of this “Back To Basics” post.

There are three primary ways to use COM objects from managed code. They all boil down to the same underlying mechanism, but each has its merits.

1. Visual Studio “Add COM Reference”

Visual Studio "Add References Dialog"

Visual Studio makes it easy to “add a reference” to a COM library. I’m using double quotes because it’s not really the COM library that you’re referencing. When you choose an assembly from the preceding dialog, Visual Studio generates for you a managed interop assembly, which contains type declarations required to access the COM library. Your managed project doesn’t depend directly on the COM library and doesn’t drag it along as a dependency: it’s entirely possible that when you deploy your managed project, the COM library will not be available on the target machine, and will require a separate installation step.

Once you have a reference to the interop assembly, your managed code can simply instantiate classes and call methods through interfaces. There is no special interop magic sprinkled around your project. Here’s an example of using the Microsoft.Office.Word.Interop assembly to create a Word process and make it visible:

// Application comes from the Microsoft.Office.Word.Interop namespace
Application app = new Application();
app.Visible = true;

2. The Tlbimp.exe Command-Line Tool

Tlbimp.exe is a command-line tool that generates a managed interop assembly from a COM library. It’s useful for customizing the generated assembly (e.g. adding a strong name) and for automating the process of creating the interop assembly. The following command will generate an interop assembly from a COM type library:

> Tlbimp.exe ComLibrary.tlb

(By the way, there is even a managed API, TypeLibConverter, that you can use programmatically to generate interop assemblies. Wow, the possibilities.)

3. No-PIA (Type Embedding/Type Equivalence)

If you inspect the interop assembly with a tool like .NET Reflector, you’ll notice that it simply contains a bunch of types and attributes. There’s nothing special about the assembly itself. This means you can simply copy the types from the interop assembly to your main managed project and use them directly. Indeed, that’s what Visual Studio does by default (starting with .NET 4.0), and it’s controlled by the “Embed Interop Types” property on the interop assembly reference.

"Embed Interop Types" Property

If Visual Studio can do it, so can you. In some cases, you don’t have neither the interop assembly nor the type library for the COM object you’d like to use. Or maybe the type library was generated from another .NET assembly, and Tlbimp.exe refuses to import it. In these cases, you can simply specify the COM interface by hand and add a couple of attributes. For example:

[ComImport]
[Guid("<YOUR INTERFACE GUID HERE>")]
interface ICalculator
{
  int Add(int a, int b);
  int Sub(int a, int b);
}

For any marshaling customizations, you can use the standard [MarshalAs(...)] attribute on parameters and return values. This approach is highly customizable, very flexible, and means you’re only importing the COM types you actually intend to use. It’s also quite error-prone, much akin to crafting P/Invoke signatures by hand. Fortunately, pinvoke.net, the website that brings you well-cooked P/Invoke signatures, has also a large number of COM interface declarations ready for embedding in managed code.

Summary

In closing, using COM objects from managed code is quite easy. You can use interop assemblies or embed the interop types in your own project. There are many more subtleties involved, which we haven’t covered here. For much more detail, “.NET and COM: The Complete Interoperability Guide” by Adam Nathan is still (after almost 12 years!) a very comprehensive and useful resource, although it doesn’t cover newer features like C#’s dynamic and No-PIA.


I am posting short links and updates on Twitter as well as on this blog. You can follow me: @goldshtn

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

2 comments

  1. Belikov SergeyDecember 29, 2013 ב 11:37 AM

    If you need to support multiple versions of Office (and in real world Office 2003 is still there), the only viable way is use something like netoffice which handle all that ugly COM things inside.
    I hoped that Windows and Office will give us .net API’s, but unfortunately it seems that Microsoft is doesn’t love .net anymore.

    Reply
  2. John GwynnDecember 30, 2013 ב 6:40 PM

    Count me in as another greybeard COM hater and all things ATL (can you say thread local storage?) and the 9 billion names of string!

    Reply