However much we try, we just can’t ignore the vast body of technology surrounding Microsoft’s Component Object Model, or COM. Over the years, this model has been the cornerstone of so much Microsoft-related development that we have to take a long, hard look at how we are going to integrate all that technology into the new world of .NET.
This chapter begins by taking a brief backward glance at COM, and then compares it with the way that components interact in .NET. It also takes a look at the tools Microsoft provides to help link the two together. Having looked at the theory, you then try it out by building a few example applications. First you take a legacy basic COM object and run it from a Visual Basic 2005 program. Then you repeat the trick with a full-blown ActiveX control. Finally, you run some Visual Basic code in the guise of a COM object.
Important | More information on how to make COM and VB6 code interoperate with the .NET platform can be found in Professional Visual Basic Interoperability: COM and VB6 to .NET (Wiley, 2002). |
As all that is done, try to remember one thing: COM is, to a large extent, where .NET came from. In addition, with all the time and resources that have been invested in this technology, it is important to consider the best ways to both maintain these investments and integrate them into new investments being made.
Before looking into the COM-.NET interoperability story, it’s important to understand COM’s main concepts. This section doesn’t attempt to do more than skim the surface, however. While the basic concepts are fundamentally simple, the underlying technology is anything but simple. Some of the most impenetrable books on software ever written have COM as their subject, and we have no wish to add to these.
COM was Microsoft’s first full-blown attempt at creating a language-independent standard for programming. The idea was that interfaces between components would be defined according to a binary standard. This means that you could, for the first time, invoke a VB component from a VC++ application, and vice versa. It would also be possible to invoke a component in another process or even on another machine, via Distributed COM (DCOM). You won’t be looking at out-of-process servers here, however, because the vast majority of components developed to date are in-process. To a large extent, DCOM was fatally compromised by bandwidth, deployment, and firewall problems and never achieved a high level of acceptance.
A COM component implements one or more interfaces, some of which are standards provided by the system, and some of which are custom interfaces defined by the component developer. An interface defines the various methods that an application may invoke. Once specified, an interface definition is supposed to be inviolate, so that even when the underlying code changes, applications that use the interface don’t need to be rebuilt. If the component developers find that they have left something out, then they should define a new interface containing the extra functionality in addition to that in the original interface. This has, in fact, happened with a number of standard Microsoft interfaces. For example, the IClassFactory2 interface extends the IClassFactory interface by adding features for managing the creation of licensed objects.
The key to getting applications and components to work together is binding. COM offers two forms of binding, early and late:
In early binding, the application uses a type library at compile time to work out how to link in to the methods in the component’s interfaces. A type library can exist as a separate file, with the extension .tlb, or as part of the DLL containing the component code.
In late binding, no connection is made between the application and its components at compile time. Instead, the COM runtime searches through the component for the location of the required method when the application is actually run. This has two main disadvantages: it’s slower and it’s unreliable. If a programming error is made (for example, the wrong method is called, or the right method with the wrong number of arguments), then it isn’t caught at compile time.
When a type library is not explicitly referred to, there are two ways to identify a COM component, by class ID, which is actually a GUID, and by ProgID, which is a string and looks something like "MyProject.MyComponent". These are all cross-referenced in the registry. In fact, COM makes extensive use of the registry to maintain links between applications, their components, and their interfaces. All experienced COM programmers know their way around the registry blindfolded.
VB6 has a lot of COM features embedded into it, to the extent that many VB6 programmers aren’t even aware that they are developing COM components. For instance, if you create a DLL containing an instance of a VB6 class, then you have in fact created a COM object without even asking for one. The relative ease of this process is demonstrated in this chapter.
There are clearly similarities between COM and .NET, so to a large extent, all you have to do to make them work together is put a wrapper around a COM object to turn it into an assembly, and vice versa.