you're reading...
COM, Interop Assemblies

Interop Assemblies – Some General Advise on Usage.

1. Introduction.

1.1 An interop assembly is a special .NET assembly which contains type information on imported COM types.

1.2 Unlike a typical assembly, it contains mostly metadata. This metadata enables managed code compilers to resolve COM object property access and method calls in code.

1.3 At runtime, the metadata of an interop assembly enables the CLR to create Runtime-Callable Wrappers (RCWs) for instantiated COM classes and to dynamically discover other COM type information.

1.4 Through over-use and lack of thorough understanding, many newbies do not know how to judiciously use interop assemblies.

1.5 Some do not know the true significance behind the well-known Primary Interop Assembly (PIA).

1.6 This blog will present some general advise on careful usage of interop assemblies in general and expound on the special purpose behind the well-known Primary Interop Assembly (PIA).

2. Varieties of Interop Assemblies.

2.1 In general, there are 3 types of interop assemblies :

  • An interop assembly that a developer creates by himself/herself from a COM type library via tlbimp.exe.
  • An interop assembly that the Visual Studio generates on behalf of the developer when the developer references a COM type library directly (either a DLL or a TLB).
  • An interop assembly that a developer creates by himself/herself from a COM type library via tlbimp.exe with a strong name key file and the /primary flag.

The first type of interop assembly is not necessarily a PIA. As mentioned, to qualify as a PIA, a strong name key file must be used together with the /primary flag.

The interop assembly generated by the Visual Studio is certainly not a Primary Interop Assembly.

Only the last type of interop assembly qualifies definitely as a PIA. To be really useful, this PIA must be registered to the registry via regasm.exe. To be even more meaningful in purpose, this PIA should be registered to the GAC.

2.2 Once a PIA is registered to the registry (via regasm.exe), if tlbimp.exe is called again on the same COM type library with a strong name key file and the /primary flag, a warning will be issued by tlbimp.exe indicating that a PIA for the type library has already been created and registered. Note however, that the interop assembly will still be created nevertheless. But one should heed this warning and not proceed to create another PIA. Defying this warning will defeat the purpose of a PIA.

2.3 Although there is no necessity to install a PIA to the GAC, it is better to do so in line with good practice. After all, it makes no sense to have many copies of a PIA in a machine when they all refer to the same types. Note that it is the strong name key contained inside the PIA that makes the types that the PIA contains unique no matter how many copies of a PIA there are in a machine.

2.4 Content-wise, a PIA is no different from an equivalent non-primary interop assembly. Besides being digitally signed, it is internally marked with a PIA-specific custom attribute (System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute).

2.5 You can see this attribute by using ildasm.exe to examine the PIA’s metadata. The following is a sample :

.assembly Interop.SomeCOMServer
  .custom instance void [mscorlib]System.Runtime.InteropServices.TypeLibVersionAttribute::.ctor(int32,
    = ( 01 00 01 00 00 00 00 00 00 00 00 00 )
 .custom instance void [mscorlib]System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute::.ctor(int32, int32) 
    = ( 01 00 01 00 00 00 00 00 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string)
    = ( 01 00 24 38 63 64 31 32 37 33 32 2D 65 61 63 61   // ..$8cd12732-eaca
        2D 34 38 38 35 2D 62 62 65 31 2D 35 64 64 36 34   // -4885-bbe1-5dd64
        61 61 30 65 33 64 65 00 00 )                      // aa0e3de..
  .custom instance void [mscorlib]System.Runtime.InteropServices.ImportedFromTypeLibAttribute::.ctor(string)
    = ( 01 00 14 53 61 6D 70 6C 65 43 4F 4D 53 65 72 76   // ...SampleCOMServ
        65 72 30 32 4C 69 62 00 00 )                      // er02Lib..

3. The Crucial Difference Between COM and .NET in terms of Type Identity.

3.1 Before going any further, I need to expound on the crucial difference in the definition of identity between COM and the .NET Framework :

  • In COM, a type is solely identified by its GUID. As long as the GUID is the same, it is the same type.
  • In the .NET Framework, a type’s containing assembly forms part of the type’s identity. This implies that the assembly’s name, version, culture and public/private key pair matters.

3.2 Now as far as the .NET Framework is concerned, the COM types contained inside an interop assembly are each represented by an equivalent managed type. And each managed type is indelibly bound to its containing assembly. Hence if we have 2 interop assemblies for the same type library, the COM types contained inside each could potentially be different types as far as the CLR is concerned.

3.3 If x number of assemblies each references its own interop assembly, we could have a situation where x number of interop assemblies are each indispensible with each assembly being able to use only the types from its own interop assembly. This is truly undesireable because the interop assemblies are all generated from the same COM type library.

3.4 This is the heart of the problem that a PIA is designed to resolve. A PIA is meant to be generated (and digitally signed) once by the developer of the original COM component and deployed multiple times by client applications.

3.5 Let me explain the problem via some examples given in section 4 below.

4. Example Scenarios.

4.1 Take, for example, the following scenario :

  • A developer creates 2 class libraries. The 2 class libs are not strong named.
  • Each class lib references a common COM type library via the IDE. Hence each class lib has its own IDE-generated interop assembly for the COM type library.
  • A managed main application references both class libs and makes a local copy of each class lib in its folder.
  • Now because each class lib references its own interop assembly and both interop assemblies have the same name, eventually only one interop assembly gets copied to the main app’s folder.
  • At runtime everything usually works out correctly. It worked out correctly because each class lib referred to the same interop assembly with the same name, version and culture. No public key tokens are compared.

4.2 Now take a different approach to the above scenario :

  • This time. the developer strong names each class lib.
  • The IDE will likewise strong name each class lib’s interop assembly with the respective class lib’s key pair.
  • This time, the two interop assemblies are no longer identical. They are different in their digital signatures.
  • When the class libs are referenced by a main client application, only one interop assembly will be copied to the client application’s folder since the interop assemblies have the same name even though they are different as far as .NET is concerned.
  • Then at runtime, System.IO.FileLoadException will occur because one of the interop assemblies (with a specific public key token) will not be found.
  • One way out of this problem would be to install both class libraries and each of their interop assemblies (4 files in total) into the GAC.

4.3 I hope you will see how the examples in 4.1 and 4.2 illustrate the usefulness of a PIA :

  • Being already digitally signed with a strong name key pair, a PIA’s identity remains the same no matter how many clients references it. This means that the (COM) types that it contains will get used by all referencing managed assemblies.
  • In fact, if a PIA gets installed into the GAC, client applications need not even bother to retain a copy of it in their main folder.

5. In Summary.

5.1 Should we always reference an interop assembly generated via tlbimp.exe or should we just directly reference a COM type library ?

5.2 One factor to consider is whether the type library is referenced by more than one assembly and whether there is a need to strong name any of the assemblies.

5.3 In the simple case where the referencing assemblies are never strong named (as in the case of example 4.1), there should be no problems in locally sharing a common interop assembly.

5.4 But in line with good practice, it is always a good idea to generate a digitally signed PIA and install it into the GAC and have all assemblies reference this central PIA.

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: