In this post, I'm about to deal with the use of COM components inside a .Net project.
Access to Windows or third-party API is commonly made through COM, and when you're working on a .Net project, this is typically done by adding a reference to the project (Project, Add Reference... menu, COM tab). Visual Studio then creates an assembly that contains the stub of interop code for the COM Type Library.
Unfortunatelly, each assembly corresponds to a file on the disk, and each file is at least 64KBytes large.
In the PixVillage project, the installation package need to be as small as possible in order to reduce download time and bandwidth, so we looked after a way to reduce the number and size of COM Interop assemblies.
A (quite) simple way to achieve both these goals is to use Lutz Roeder's Reflector to generate the C# code of the COM stub directly from the assembly IL.
To do so (xxx represents the name of the Type Library) :
- Start Reflector,
- Find the COM Interop assembly (refer to the properties of the reference : the file name should be "Interop.xxx.dll"),
- Open the COM Interop assembly in Reflector,
- Expand both the assembly (Interop.xxx) and file (xxx.dll) nodes in the treeview,
- Select the node of the Type Library base namespace in the tree (the node called xxx with a { } icon),
- Press the Space bar to generate C# code for the namespace,
- Click in the right pane, select all code (Ctrl+A) and copy it to the clipboard (Ctrl+C),
- Paste the code in a new C# (.cs) file in Visual Studio.
Now, there might be missing references : you must add them (with the using keyword) at the very beginning of the new file. For instance, you may have to add references to the following namespace : System, System.Collections, System.Runtime.InteropServices, System.Reflection, System.Runtime.CompilerServices...
You may also have duplicate member definitions because inheriting interfaces define the same members as their parent : remove them.
The last thing to do is to clean the content of the classes (which are different from interfaces) contained in the namespace. When rendered by Reflector, a class implements one or more interfaces and contains a number of properties / methods with no code, which do not compile. To make your code compile, remove all of its base interfaces and all of its content to make it empty.
Now you might have in your code :
- A number of enums and interfaces that represents the interfaces and constant values exposed by the Class Library,
- One or more interfaces having a CoClass attribute, corresponding to the components exposed by the Class Library
- One or mode classes used by the framework to create the instances of the exposed components.
For instance, if you are importing the MSN Messenger API, you may have a IMessenger interface, a Messenger component and a MessengerClass class. In order to access the API, you will have to type IMessenger msn = new Messenger();