You've seen how to quickly create a COM object that can then have its type library imported and made available to .NET via Visual Studio .NET or the TlbImp.exe processor. When vendors want to make their components available to .NET programmers, they produce what is called a primary Interop assembly (PIA). The next section of this chapter deals with what PIAs are, how they work, and how you can make and consume them in your own code.
Overview of Primary Interop Assemblies
PIAs are unique assemblies that contain COM proxies that are ready to be used by managed code. What happens all too often is that, without a PIA, every developer creates his own Interop assembly with TlbImp or Visual Studio .NET and is free to make changes and distribute that component with the application.
A PIA is made unique by virtue of being signed with a key pair file. Key pairs are generated using the sn.exe utility that is part of the Framework SDK. Rather than every developer creating their own COM wrappers around a vendor's COM components, the vendor can supply a single unique assembly that contains the vendor-approved COM wrappers that might be optimized to work with the vendor's product.
Working with PIAs
Primary Interop assemblies can be distributed with the application that contains the related COM components or they can be distributed as separate downloads from the vendor's website. If you aren't sure whether a particular COM component has a PIA, contact the component's vendor to double-check. If you create your own library and sign those types with your own key pair, your components could end up having compatibility issues with the vendor's PIA should the vendor produce one.
To write code that utilizes a PIA, all you have to do is add a reference to the PIA from your managed code project and use the classes and methods contained within the PIA as if they were managed. Typically any marshaling and COM compatibility issues will have been taken care of by the vendor, so most PIAs provide clean, easy-to-use implementations.
Producing and Deploying PIAs
Producing a PIA involves several steps. There are both suggested guidelines for features that a PIA should provide, and hard requirements that are enforced by the Framework itself. The following is the list of requirements that Microsoft lists as conditions that must be met for an assembly to be considered a valid primary Interop assembly:
There are a few other suggestions and guidelines available for creating primary Interop assemblies. One of the suggestions that I strongly agree with is that all PIAs should have a namespace that ends with Interop. This would prevent future conflicts with a pure managed solution published by the same vendor.
The physical generation of a primary Interop assembly can be done one of two ways. The hard way involves manually writing the C# code that contains all the appropriate COM attributes (refer to Table 13.2 earlier in the chapter for some common COM attributes) to expose the types to COM. The second method involves using TlbImp.exe to create a managed assembly based on the information contained in a COM type library. Because TlbImp is the most common and the easiest to use, that's the method that will be discussed here.
To create a primary Interop assembly, it must be signed with public key encryption in the same way you sign your own assemblies for which you are providing a strong name.
To create a managed wrapper around a COM component, provide a signature key, and indicate that the assembly will be a primary Interop assembly, you can use the following syntax:
TlbImp filename /primary /keyfile:keyfilename /out:assemblyfilename
This code will create a new assembly called assemblyfilename that is marked as a PIA, signed with the key pair found in the file keyfilename. If you want to change the namespace to reflect that the assembly is a PIA, you can use the /namespace option, as shown in this sample execution:
TlbImp ComComponent.tlb /primary /keyfile:ComComponent.snk /namespace:COMCompany.ComComponent.InterOp /out:COMCompany.ComComponent.InterOp.dll
The preceding command creates a new primary Interop assembly that is a managed wrapper for the functionality contained in the ComComponent type library, and changes the namespace of the managed classes to COMCompany.ComComponent.InterOp.dll.