Recently at work I was called on to fix a bug regarding primary interop assemblies created with tlbimp. Our issue was in finding out how to get the correct company name and version info into the interop assemblies, since tlbimp does not let you specify those. Since it was an interesting problem and an interesting fix, I figure that it would be a good thing to blog about.
Primary Interop Assemblies:
Primary interop assemblies are created to allow COM components to be consumed by managed components. The primary interop assembly (PIA from now on) contains metadata to describe the functionality of the COM components. The PIA contains no implementation, just type definitions and instructions on marshaling. Actually, the above description also describes a normal interop assembly. A primary interop assembly is all of the above, plus it is the only “official” description of the COM components. The PIA must be created and signed by the company that produces the original COM components to be considered a PIA.
Tlbimp.exe is a tool provided by Microsoft as part of the .NET Framework for creating interop assemblies. Tlbimp provides a large number of options, but no option for setting the company name or version info on the created interop DLLs. Let us say that we have a fictional DLL called comstuff.dll that we want to create a primary interop assembly for. We would use the following command line to create it:
C:\>tlbimp comstuff.dll /primary /keyfile:myPublicKey.snk /out:comstuff.interop.dll
But the comstuff.interop.dll created above does not have version and company name information and there is no way to specify them through tlbimp.
Our plan of attack:
Now it’s time to get into the details of how we are going to actually get our company name and version info into this DLL. Our basic plan of attack is as follows:
1. Compile an .RC file into a .RES file containing our version information
2. Ildasm the DLL to disassemble it into intermediate language
3. Ilasm the intermediate language and the new .RES file to compile a new DLL with the correct version information
The resource file:
All that the .RC file needs to do is define a few specific constants. Below is an example some of the entries in just such an .RC file.
#define VER_FILEDESCRIPTION_STR "Interop Library"
#define VER_COMPANYNAME_STR "Microsoft Corporation"
#define VER_PRODUCTVERSION_STR “220.127.116.11”
There are lots of other things that can be set at this point, but I’ll leave that up to the readers to find out what you need to add. Plus it depends on exactly what things you need to set on the file that you are creating. Next, you need to compile this into a .RES file using the rc.exe tool. Verinfo.rc is the .RC file that we created above.
C:\>rc.exe /r /fo versioninfo.res verinfo.rc
Ildasm.exe is a tool to break down a PE (portable executable) file that contains managed code and exports it into an .IL file which can later be reassembled back into a PE file. Also, although it is not pertinent to this blog topic, just type ildasm followed by a PE filename to get a display of all the metadata for that PE, very handy! Below is the command line that we will use to disassemble our DLL.
C:\>ildasm /nobar comstuff.interop.dll /out=comstuff.interop.il
Ilasm.exe is the polar opposite from ildasm. It takes .IL files and compiles them back into PE files. Also, it allows us to specify a .RES file for the new PE file, which is what we are going to be doing with it.
C:\>ilasm comstuff.interop.il /OUTPUT=comstuff.interop.dll /RESOURCE=versioninfo.res /DLL
So now we end up with an interop DLL (comstuff.interop.dll) that contains everything from the original DLL plus the new stuff specified in our .RC file. Quite the process! I like to wrap all this up into a .CMD file which you can call it from a .vcproj file as part of your normal build process.