The Class Factory and IClassFactory

Telling COM where a server lives on the file system is one thing, but to make the component useful, that server has to provide a way for COM to request the creation of a new object. This mechanism has two parts. First, the server has to implement a class factory, which is a special type of object that literally manufactures another object of a particular CLSID, as "factory" implies. Second, the server has to expose this class factory to COM, and doing this differs between a DLL and an EXE, just as with the self-registration mechanisms.

The number one concept to understand is that a class factory, while itself an object, is not the root object of the component. Every instance of a class factory is associated with a single CLSID and exists strictly to facilitate the creation of the root object of a component of that CLSID, as illustrated in Figure 5-4. This is part of the COM standard. A single implementation of a class factory, such as a C++ object class, can be written to support any number of CLSIDs, but each instance of such an implementation can create objects for only one CLSID. The reasons for this will be clearer in a moment.

Figure 5-4.

A class factory object exists to create other objects of a specific CLSID.

A class factory object gets its name because it must implement at least IClassFactory (and optionally IClassFactory2, which we'll see later).


interface IClassFactory : IUnknown
{
HRESULT CreateInstance(IUnknown *pUnkOuter, REFIID riid
, void **ppv);
HRESULT LockServer(BOOL fLock);
};

The LockServer function increments or decrements a lock count, which allows a client to keep a server in memory even when it is serving no objects; see "Unloading Mechanisms" later in this chapter for more details on this function. Note that you may run into the archaic belief that you need not implement this function, which is dead wrong. See the NOTE on page 242.

CreateInstance does what it says: it creates an uninitialized instance of the object associated with the class factory, returning in *ppv a pointer to the interface identified with riid. (That is, a QueryInterface is built into CreateInstance.) The pUnkOuter argument is a pointer to the controlling unknown if the object is being created as part of an aggregation (as described in Chapter 2). This pUnkOuter is the standard means through which a new object from a custom component is given the controlling unknown pointer. If this pointer is non-NULL and the object doesn't support aggregation, CreateInstance should return CLASS_E_NOAGGREGATION. Otherwise the object should verify that the caller has requested IID_IUnknown according to the aggregation rules, returning E_NOINTERFACE if not. E_NOINTERFACE is also the return code when the requested interface is not otherwise available, and E_OUTOFMEMORY is a common error to indicate that the object could not be created.

Now you will notice that CreateInstance is not passed a CLSID because the association between CLSID and class factory happens elsewhere in the server. (See the section "Exposing the Class Factory" on page 237.) Other than that, the idea of a class factory and the CreateInstance function is like the C++ new operator, and in fact, C++ implementations of class factories typically use new internally. For example, we've already seen code in the CreateAnimal function of the Chapter 2 REUSE sample that does exactly what is being described here:


HRESULT CreateAnimal(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
CAnimal *pObj;

if (NULL!=pUnkOuter && riid!=IID_IUnknown)
return ResultFromScode(CLASS_E_NOAGGREGATION);

pObj=new CAnimal(pUnkOuter);

if (NULL==pObj)
return FALSE;

if (!pObj->Init())
return FALSE;

return SUCCEEDED(pObj->QueryInterface(riid, (PPVOID)ppv));
}

This code is almost exactly like most CreateInstance implementations you'll see and write. What is special about the class factory object is that we can expose an IClassFactory pointer outside the server through a standard mechanism, whereas it's much harder to directly expose some arbitrarily named function like CreateAnimal. In fact, IClassFactory is precisely how you would encapsulate a function such as CreateAnimal behind a binary standard so that it can be used from outside the server. In such a case, IClassFactory::CreateInstance would do nothing more than call CreateAnimal, and it is the COM interface mechanism that allows the server to expose the pointer to this function in a standard way. This is how COM makes it simple to encapsulate legacy code in OLE objects.

IClassFactory, therefore, eliminates the need for any client to know the exact name of the creation function for some type of component—the client need only know the CLSID, which can be retrieved from the registry. So we can now see that the only thing standing between the server pathname in the registry and the class factory itself is a way for COM to get hold of that factory's IClassFactory pointer. This is the subject of "Exposing the Class Factory" beginning on page 237.


Uninitialized Objects and Initialization Interfaces

The previous discussion describes IClassFactory::CreateInstance as creating an uninitialized object from the perspective of the client. This means that the client's first act should be to pass any additional information that will suitably initialize the object. Of course, some objects need no initialization, so that CreateInstance returns, in those cases, ready-to-use objects. Other objects, however, will support at least one interface that contains an initialization member function. IPersistStorage::InitNew and IPersistStorage::Load are interfaces of this kind; one tells an object to initialize with a new state (InitNew) while the other tells an object to initialize from a previously saved state (Load). Another example of an initialization interface is IOleObject::SetClientSite, through which a compound document object is given access to the functionality in the container site, which the object may need for initialization. In the course of this book, we'll see other examples of initialization interfaces.