Introduction
I’ve had several questions lately asking specifically what a COM component is, what follows is hopefully meant to provide some quick answers to those questions.
Fundamentally, a COM component (or component-object, or COM object) is just a reusable piece of code and data in binary form that has the following 4 attributes:
- Encapsulation – Components must hide the details of how they are implemented.
- Interfaces – Only pointers to interfaces are used to interact with them.
- Language independence – Components written in one language can connect to and use components written in another language.
- Dynamic linking – Components must link dynamically.
Encapsulation
Encapsulation prevents the details of how a component is implemented from leaking out. One of the benefits of COM is the ability to load and unload components at runtime which potentially means unloading one component and replacing it with another. If the first component were to expose any details of its implementation, then code which uses that component could make assumptions based on that. Those assumptions may not hold true for the next component whose implementation is different and could result in undefined behavior or a crash. Therefore the implementation details must be hidden and interfaces help do that.
Interfaces
COM components use interfaces to define the interaction between the client and the component object. Interfaces help provide encapsulation and are the sole method of interacting with a component object. COM interfaces are nothing more than a the definition of a specific memory structure that contains an array of function pointers. It’s a binary definition which is all that is needed to interact with the component. Everything else is unnecessary.
Language Independence
Component objects can be implemented in any language that can be made to create and use the array of function pointers. Given a COM interface, any component written on one language can use a component written in another language. However, the component object MUST hide the all of the details of the language that it is implemented in. Exposing the implementation language would create dependencies between the component and the client. Those dependencies could prevent it’s use by clients that were not implemented using the same language.
For example, given the following:
interface IFoo : p6ICom { P6COMMETHOD someMethod(mysmartptr *ptr) = 0; }
Above we see that someMethod() takes an argument which is some sort of C++ smart pointer. It’s a language specific complex type which breaks encapsulation. As a result, any client that uses this interface to access a component object will have to have knowledge of this language specific implementation. Most likely, clients written in another language will not be able to use this component. Even clients compiled with a different C++ compiler will not be able to use it.
Dynamic Linking
Dynamic linking is the foundation of several of COM’s most powerful features. COM introduced an architecture that moved us away from huge, unchanging monolithic applications. Without dynamic linking your components would need to be statically linked together, which is not any different than having a monolithic application. When a component changes, dynamic linking (and encapsulation through interfaces) allows a new component to be dropped in at runtime. Without it, you would have to re-link and possibly recompile in order to implement your changes.