COM: A Brief Introduction
Outline
A Brief History of COM Objects vs. Components COM as a (C++)++
The What and How of COM COM as CORBA-light
A Brief History of COM
The Brain Child of Anthony Williams –
outlined in two (internal) MS papers:
Object Architecture: Dealing with the
Unknown or Type Safety in a Dynamically Extensible Class (1988)
On Inheritance: What It Means and How To
History (Cont.)
Origins of COM were OLE (Object Linking
and Embedding) 1 that shipped with Windows 3.1 (1992)
The first public version of COM shipped
in OLE 2.0 (1993).
DCOM (Distributed COM) was released
in 1996 in answer to CORBA. (We’ll ignore it.)
COM+ was released along with Windows
Objects vs. Components
“Object Oriented Programming =
Polymorphism + (Some) Late Binding + (Some) Encapsulation + Inheritance
Component Oriented Programming =
Polymorphism + (Really) Late Binding + (Real, Enforced) Encapsulation +
Interface Inheritance + Binary Reuse”
COM as (C++)++
(Adapted From [2]) If you “get” this, it’s all down hill from
here.
In C++, in particular, the linkage model
makes binary distribution and reuse difficult.
Consider: You’ve written a class that’s
Challenges of Distribution
Imagine we distribute the source for the
class (as is common in C++ class libraries).
If each application that uses it statically links it,
it gets duplicated (waste) and is impossible to update/fix in the field without redistributing a new application.
If it’s packaged as a shared library/object, the
lack of binary standardization moves the problem to one of interoperation.
Challenges of Encapsulation
Assume we side-step the compiler/linker
trouble. The coast still isn’t clear. The C++ standard also lacks any standard definition for binary encapsulation.
So changes to the internals of an object
that don’t change it’s interface can (and do) break code that uses the
object.
Versioned Libraries
A quick look in /usr/lib or %WINDIR% will likely reveal a number of “identical”
libraries with different versions.
libFoo.so.1 libFoo.so.2
With enough diligence the library
developer can insulate applications from change buy explicitly versioning the
library.
Interface v. Implementation
C++ supports separation of interface and
implementation at the syntax level – not at the binary level.
So changes of the implementation are “seen”
by clients.
We could hide the actual implementing
class behind an opaque pointer in the interface exposed to the client and
delegate interface calls through this pointer to the “real” object.
Easy for simple, cumbersome for complex
Abstract Classes as
Interfaces
With three assumptions, we can
use abstract classes to solve these
problems:
1. C-style structs are represented
identically across (C++) compilers.
2. All compilers can be forced to use common call conventions.
3. All compilers on a platform use
vtbls and vptrs
Assumption 3 is critical, and turns
out to be not unfounded, as nearly
all C++ compilers use vptrs and
vtbls.
For each class the compiler
generates a (static) array of func
pointers to it’s members (it’s vtbl).
Each instance of each class has a
IsearchableString
Example
class ISearchableString {
public:
virtual int Length(void) const = 0;
virtual int Find(const char *s) = 0;
};
vptr
vtbl
Example (cont.)
class SString : public
ISearchableString {
public:
SearchableString(const char *s); ~SearchableString(void);
int Length(void) const; int Find(const char *s); };
SString
vptr
vtbl
Instantiating an Abstract
Class
Clearly the client can’t instantiate
an ISearchableString – it’s pure
abstract, nor do we want them
instantiating a SString – that
breaks (binary) encapsulation.
So we need a factory method – and
Virtual Destructors
Unfortunately, there’s a problem – our
class lacks a virtual d’tor – so calls to
delete will use the (default) d’tor on the ISearchableString class.
We can’t add a virtual d’tor to the
abstract class because different
compilers put dtor’s in different places in the vtbl. (blech)
So we add a virtual “Delete” method to
What is COM
A “substrate” for building re-usable
components.
Language neutral
it’s easier to use in C++, but can be used from any
language that can generate/grok vtbl’s and vptrs.
Interfaces are defined in COM IDL (IDL+COM
extensions for inheritance and polymorphism)
OS Neutral
commercial Unix implementations, and MS
supports COM on Mac System (OS X?)
Using only on the COM spec, we (OMKT) rolled our
Interfaces
Interfaces are uniquely identified by
UUID’s (often called GUID’s – the terms are equivalent) called their IID
(interface ID).
Implementers of an interface are
uniquely identified by a UUID called their CLSID (class ID).
All COM objects implement the
IUnknown
Provides three methods:
HRESULT QueryInterface(IID iid, void **ppv) ULONG AddRef(void);
ULONG Release(void);
AddRef and Release are for resource
QueryInterface
QueryInterface is essentially a run-time
cast – it allows you to ask a component if it implements a specific interface.
If it does, it returns a pointer to that
interface pointer in ppv.
Think of it as a compiler/language neutral
HRESULT
This is language neutral – so no
exceptions. HRESULTS are a packed bit field return value used all over COM.
Honestly it’s one of the ugliest parts of COM.
The most used return value is defined as
S_OK (success, ok), the other is E_FAIL (error, failure) but there are others.
There are macros SUCCEEDED() and
Instantiating Objects
So as developers, we have interfaces
(defined in IDL) for the components
available in a library/on the system.
How do we actually obtain an
instance of an object we want to use?
In COM this is termed Activation – there
Activation and the SCM
The SCM manages the mapping between
IIDs, CLSIDs, and implementations.
You can ask the SCM for a particular
CLSID and it will instantiate an instance and return it’s interface pointer.
CoGetClassObject()
There’s an additional layer of indirection
through ProgIDs – strings of the form
Activation (cont.)
Sometimes you want “an
implementation of the following
interface that meets some set of
constraints”
enter category IDs (CATIDs)
You can define a set of categories,
COM as CORBA-light
COM provides a very efficient in-process
component model.
Once past the initial COCreateInstance and
QueryInterface calls, each method call is
simply a call-by-func-pointer call, essentially free.
Instantiating a component doesn’t
There’s Much, Much More
COM is specific about many topics
that C++ (and other languages)
are not. It specifies:
Execution environment options
(so-called Apartments)
Inter-process Marshalling
Remote Object Activation mechanism
and protocols
The Definitive References
[1] The Component Object Model Specification
Microsoft and Digital Equipment Corp,
1992-1995
www.microsoft.com/com/resources/comdocs
.asp
[2] Essential COM