From a client's point of view, an instance of a particular COM component is created as follows:
CoCreateInstance() to create
the object:
HRESULT stdcall CoCreateInstance
( [in] REFCLSID clsid
, [in] IUnknown* pIUnknownOuter
, [in] DWORD dwClsContext
, [in] REFIID iid
, [out] void** ppv
);
If successful, the last argument will be filled-in with the interface
pointer of the newly created object. (There's a lower-level and more
flexible interface to object creation that we won't go into here.)
What does the component author need to provide in order for clients to
be able to create instances using CoCreateInstance()? The answer
is an implementation of a class factory, a component instance
constructor for that particular COM component.
There's a standard COM interface for class factories:
[
object,
uuid(00000001-0000-0000-C000-000000000046),
pointer_default(unique)
]
interface IClassFactory : IUnknown {
[local]
HRESULT CreateInstance(
( [in, unique] IUnknown * pUnkOuter
, [in] REFIID riid
, [out, iid_is(riid)] void **ppvObject
);
[local]
HRESULT LockServer([in] BOOL fLock);
}
The method of interest here is CreateInstance():
HRESULT CreateInstance
( [in, unique] IUnknown * pUnkOuter
, [in] REFIID riid
, [out, iid_is(riid)] void **ppvObject
);
Given an interface identifier and a pointer to an interface pointer as
arguments, CreateInstance() tries to create a COM object with the
desired IID (failure or otherwise is reported back via the
HRESULT return value). But how do we get at the class factory
corresponding to a particular CLSID in the first place? Using
CoGetClassObject() from the COM library:
HRESULT CoGetClassObject
( [in] REFCLSID rclsid
, [in] DWORD grfContext
, [in] COMSERVERINFO* pServerInfo
, [in] REFIID riid,
, [out] void** ppv
);
It locates and creates the class factory corresponding to
rclsid. (Notice that CoGetClassObject() takes a REFIID
argument specifying the factory interface we want to access. This is
usually the interface id of IClassFactory, but doesn't have to
be, IClassFactory is just the common/standardised way of
exposing component constructors).
Assuming the component is provided in a DLL (and we're looking for an
in-process instance), the system implementation of
CoGetClassObject() will perform the following steps when trying
to locate and create a class factory:
HRESULT DllGetClassObject
( [in] REFCLSID rclsid
, [in] REFIID riid
, [out] void** ppv
);
The CLSID and IID passed to DllGetClassObject() are identical to
the arguments passed to CoGetClassObject(). Same goes for the
pointer to the interface pointer. If successful,
DllGetClassObject() returns the interface pointer of the class
factory, which CoGetClassObject() then passes back to its caller.Having got the interface pointer for the class factory back, the client
can now invoke its CreateInstance() method.
The COM library provides a convenience function,
CoCreateInstance(), which hides the class factory from programmer
view. It combines CoGetClassObject() and CreateInstance()
into one, just returning the component instance (and releasing the
factory.)
To make a COM class generally visible on a machine, you need to register it in the system-wide mapping which associates a CLSID withe the code that implements the component class.
Registration is normally done by the installer program, or assuming
the COM class is packaged up in a DLL, through the use of the
regsvr32 util. Provided the DLL follows the COM registration
conventions by naming the DLL entry points that registers (and
unregisters) as follows:
HRESULT DllRegisterServer(void);
HRESULT DllUnregisterServer(void);
regsvr32 will perform the registration for you. The
implementation of regsvr32 itself is straightforward: load the
DLL, followed by a lookup and invocation of one of the above two entry
points.
COM also defines/suggests a set of guidelines to ease the registration of process servers (EXE).