In addition to using COM objects from .NET clients, you can use .NET objects in the COM world. The process for exposing .NET classes as COM objects is complex because interacting with COM at the C++ level is difficult. For this reason, this section introduces the topic but leaves the practical implementation of .NET-to-COM code for more advanced texts.
Once again, wrapper classes are used, only this time they are called COM Callable Wrappers (CCWs). In effect, a CCW puts a COM layer onto a .NET object so the .NET object behaves in exactly the way a COM object is expected to behave. The process is shown here:
The CCW exposes all the interfaces expected by clients using COM, such as IUnknown and IDispatch, and it lets the client code manage its lifetime in the normal COM manner.
COM objects have a particular set of characteristics, and .NET types need to follow some rules if they’re to be exposed as COM objects using COM Interop. Here’s a summary of what the .NET type has to do:
It has to supply a default constructor—one that doesn’t take arguments—because COM objects are always created uninitialized and there’s no standard way to pass over initialization data. For this reason, it must be possible to create .NET objects uninitialized if they’re to be used as COM objects.
The type’s assembly must be signed with a strong name. See the upcoming sidebar “Names and Signing” for details on strong names and how to use them.
The type’s assembly must be placed where the CLR can find it. See the upcoming sidebar “Installing Assemblies” for more details.
The correct COM-related registry entries must be made for the .NET object. This is done for you automatically if you’re using Visual Studio.
Assemblies are normally identified by their name, version number, and possibly locale information. This is adequate for private assemblies that will be used only within a single application. However, it isn’t good enough for those that will be used more widely because two people could use the same name for their assemblies, resulting in lots of potential for confusion.
To make assemblies unique, they should be given a strong name, which consists of the text name, version, and locale information plus a public key and a digital signature. Every key generated using Public Key Encryption is unique, so using keys and digital signatures serves both to provide a unique identifier for an assembly and a way to verify the assembly owner or creator.
COM requires that components be uniquely identified, and it uses GUIDs to accomplish this. .NET strong names fulfill the requirement for unique component identification, and they also provide information about the component’s originator, which GUIDs do not.
Assemblies are typically installed in one of two places. Private assemblies, which are intended for use by a single application, can be placed in the directory where the executable lives or any directory directly underneath. Shared assemblies are installed into the Global Assembly Cache (GAC), which is a per-computer repository for assemblies that need to be shared. You don’t manually copy assembly files into the GAC; you use the tools provided by the .NET Framework for managing the cache (such as gacutil.exe).
Assemblies must live in one of these two locations because they are where the CLR looks for assemblies when it needs to load them at run time.