COM Components vs. C++ Classes

In describing COM's custom component architecture and the mechanisms that make it work, I mentioned that IClassFactory::CreateInstance, and thus also CoCreateInstance, are something like C++ new operators for COM components. A fair number of similarities exist between C++ classes and COM components, especially when you're designing an application or other project using object-oriented principles. The analysis and design that you can use to create a C++ object hierarchy based on classes and class inheritance can also be used to create a COM component hierarchy based on CLSIDs, containment, and aggregation. Object-oriented analysis and design is a useful process to find encapsulated elements of any system, which can be turned into objects of some sort within a hierarchy. The implementation issue is how exactly to express those objects in code.

The process called componentization is the breaking up of a large application or system into individual COM components. It's really a matter of first identifying those elements you can isolate as separate objects and then matching each object's functionality and content to the various OLE technologies and interfaces through which you can integrate those objects.

A comparison of how "objects" work both in an object-oriented language such as C++ and in COM helps us understand why COM is designed like it is and why it's different. From experience, I know that the object-oriented paradigm in COM differs somewhat from traditional object-oriented languages, and this can seem rather intimidating.

The main reason why COM uses a different paradigm is that it's trying to solve the problem of integrating components that are developed, deployed, and revised independently so that new components can be instantly and robustly integrated into a running system. COM solves problems at the run-time binary level, through the binary standard of interfaces, and is concerned with components and objects that live and execute outside the boundaries of an application—that's what component integration is all about. Programming languages such as C++, on the other hand, are designed for solving programming problems within the scope of a single application on the source code or compile-time level. Few mechanisms are available for run-time integration of C++ objects implemented in DLLs, let alone the integration of objects in separate processes that might be written in different programming languages. Allow me to indulge in an analogy to illustrate the differences between COM and C++. Let's go traveling.

Suppose I'm a C++ application that lives in Rugby, North Dakota (the geographic center of North America), and I am bounded by the border of the continental United States, as illustrated in Figure 5-8 on the next page. I can visit freely any of 48 states, no questions asked, by driving along an interstate. Access is fast and easy, although I am subject to the laws of each state I drive through. I can also drive into Canada or Mexico to buy their goods and use their services, but I have to stop at their borders and answer a few questions; travel is a little slower but still quite easy. In programming terms, I can freely use any object class within the boundaries of my application as long as I obey the access rights of those individual objects. I can also use objects implemented in DLLs, but there is just a little more work involved in getting across the DLL boundary, even to my own DLL, such as Alaska.

Figure 5-8.

Travel within North America is fairly painless.

I might live happily for a long time while restricting my travels to a single continent. But there are six other continents and many other countries on the planet, and at some point I might want to visit them. Getting there is not easy—I have to transfer flights, go through customs, and show my passport. If I want to travel to a distant destination, such as Antananarivo, Madagascar, I would have to fly to Chicago and then to London, switch carriers to get to Nairobi, Kenya, and then catch a final flight to Antananarivo. On each segment of my journey, I would probably fly on a different airline in a different airplane (or I might be forced to travel only by boat or train) and walk through customs offices in three different countries. If I step out of line anywhere, I might find myself in trouble on the other side of the globe.

As a C++ application, I experience the same difficulty in using C++ objects implemented in other applications (countries) or code that is otherwise separated by a process boundary (oceans) or a network boundary (planets), as illustrated in Figure 5-9. The best I can hope for is to become intimately familiar with the protocols and customs of each application along my way, knowledge that can apply only to those specific applications. When I want to use the services of a different application, I must learn another new interface.

Figure 5-9.

Travel abroad involves much more time, effort, and knowledge.

COM offers you membership in the Component Club, which makes travel abroad much easier. This Club essentially standardizes the protocols for visiting any other country, so you have to learn only one set of rules. The Club offers nonstop flights to many countries (in-process components) and at worst one-stop flights to any other destination on the earth (local components) or on the moon or any of the other planets for that matter (remote components). As a member of the Component Club, travel is as easy as showing your membership card and hopping on a plane bound for whatever destination you choose. No matter where you are, there's a flight departing to any destination, as depicted in Figure 5-10 on the following page.

Figure 5-10.

The Component Club simplifies terrestrial and interplanetary travel.

In programming terms, you join the Component Club by using the various COM (and OLE) API functions to access a specific component without concern for where its server executes. Those API functions form the protocols you learn once, and with the upcoming capability to access remote servers, you can reach servers running on any networked machine, be it figuratively or literally on another planet.

The purpose of this little exercise was to show that C++ objects are somewhat limited in scope because access to objects, being defined by the language, restricts you to objects that live in your own application and were written in that same language. COM, on the other hand, is not concerned with languages—by encapsulating object capabilities behind interfaces and providing Local/Remote Transparency for those interfaces, COM opens up a world of components to everyone.