Creating and Initializing Objects from a CLSID

Given a CLSID, you can create objects in two ways:

CoCreateInstance is really nothing more than a convenient wrapper around CoGetClassObject and IClassFactory::CreateInstance that creates a single object. The following code shows the basic implementation of this function:


STDAPI CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter
, DWORD dwContext, REFIID iid, void **ppv)
{
HRESULT hr;
IClassFactory *pCF;

*ppv=NULL;

hr=CoGetClassObject(rclsid, dwContext, NULL, IID_IClassFactory
, (void **)&pCF);

if (FAILED(hr))
return hr;

hr=pCF->CreateInstance(pUnkOuter, iid, ppv);
pCF->Release();
return hr;
}

What the arguments to this function mean is not hard to figure out: rclsid is the CLSID of interest, pUnkOuter points to the outer unknown if this call is being made for aggregation purposes, and riid and ppv are just like the arguments passed to QueryInterface that specify the type of interface pointer to obtain from the newly created object. The most interesting argument is dwContext, which allows the caller to restrict the types of servers used for the object: it can be CLSCTX_INPROC_SERVER, CLSCTX_INPROC_HANDLER, CLSCTX_LOCAL_SERVER, or any combination of these values. The value CLSCTX_ALL is defined as the combination of all three. So a client (or an aggregate object) can choose to use only handlers or only in-process or local servers. You can play with these flags to change the order in which COM typically looks for servers (normally, in-process, handler, local).

In looking at the code for CoCreateInstance, you can see where pUnkOuter, riid, and ppv are passed to IClassFactory::CreateInstance, which is the only place they are used. This leaves the function CoGetClassObject, whose sole purpose in life is to access a class factory for a specific CLSID, returning some interface pointer to that factory. Its complete function prototype and argument list are as follows:


HRESULT CoGetClassObject(REFCLSID clsid, DWORD dwContext
, LPOLESTR pszRemote, REFIID iid, void **ppv)

Argument

Description

clsid

A REFCLSID identifying the class factory to obtain

dwContext

A DWORD identifying the allowable execution contexts

pszRemote

An LPOLESTR to the name of the remote machine on which the remote server is to run5

iid

A REFIID identifying the interface to obtain on the factory object6

ppv

A void ** in which to store the interface pointer upon return


Calling CoGetClassObject yourself is necessary in three situations:

In all of these cases, CoCreateInstance is either inefficient for the task (the first case) or unable to perform the task (the second and third cases), so you must go to the class factory directly. Be sure to call Release when you're finished with the class factory, even if you still hold interface pointers to objects you created through it.

Note once again that IClassFactory::CreateInstance (and therefore CoCreateInstance) creates an uninitialized object, so the first act a client should perform is to query for an appropriate initialization interface and call an initialization member function. But whether this really happens depends on the type of object in question because some objects need no initialization. Usually such requirements are defined (and documented) as part of the prototype of which the object class is an instance.

One last note: if CoCreateInstance or CoGetClassObject returns REGDB_E_CLASSNOTREG, check the registry. You might have forgotten to register a server, or those entries might have been corrupted. The registry is always a good place to start looking for problems when object creation fails.

5 At the time of writing this argument was nonfunctional as OLE's distributed services are not yet available. A NULL value must be passed in the meantime. When distributed services are available there will be more than just pszRemote involved to access a remote server—for example security and load balancing. This will most likely be handled through use of monikers that can dynamically determine which server to use and that can perform security checks. The pszRemote argument is really intended for later use with such monikers.

6 Windows NT 3.5 allows a client to pass IID_IClassFactory in this argument only when a local server is going to be used. This restriction will be removed in the near future.