[Previous] [Contents] [Next]

Initializing Objects

In earlier chapters, we identified coclasses by their class identifier (CLSID) and instantiated objects using the rather primitive CoCreateInstance function. CoCreateInstance is typically one of the first functions that new COM+ programmers learn, so many of those programmers overuse it until they gain some experience and insight into the richness of the COM+ namespace. Part of the problem is that some programmers see CoCreateInstance as the COM+ analogue of the new operator in C++. This parallel is further reinforced by the fact that CoCreateInstance is actually called by Microsoft Visual Basic and Java programs when the New keyword is applied to a coclass. Unfortunately, CoCreateInstance is a poor substitute for the functionality provided by the new operator in C++. For example, say that you have defined a C++ class that computes prime numbers, as shown below.

class prime
    prime(int starting_prime) : m_first(starting_number) { }
    int get_next_prime(void);

    int m_first;

The constructor for this class accepts one argument, whose value becomes the starting prime number. Each call to the get_next_prime method returns a subsequent prime number, which implies the following usage:

prime* my_prime = new prime(7);     // Calls constructor
cout << my_prime->get_next_prime() << endl; // Displays 11

Now imagine that you want to turn the C++ prime class into a coclass that implements the IPrime interface. First you need to define the IPrime interface containing the GetNextPrime method, as shown below:

interface IPrime : IUnknown
    HRESULT GetNextPrime([out, retval] int* next_prime);

Noticeably absent from this interface definition is the constructor. Typically, the client program calls CoCreateInstance to instantiate the object, automatically invoking the constructor. Unfortunately, CoCreateInstance doesn't accept an extra argument for our constructor. As you know, CoCreateInstance is a high-level object creation function that is implemented using the CoGetClassObject function. The following pseudo-code shows the implementation of CoCreateInstance:1

HRESULT CoCreateInstance(REFCLSID rclsid, IUnknown* pUnkOuter, 
    DWORD dwClsContext, REFIID riid, void** ppv)
    IClassFactory* pClassFactory;
    CoGetClassObject(rclsid, dwClsContext, NULL, 
        IID_IClassFactory, (void**)&pClassFactory);
    pClassFactory->CreateInstance(pUnkOuter, riid, ppv);

Class Objects

In most contexts, you can substitute the phrase class factory with class object, but this is not always the case. A class factory is an object that implements the two methods of the IClassFactory interface, CreateInstance and LockServer. This implementation enables CoCreateInstance to easily instantiate a coclass. However, notice that the function used by CoCreateInstance in the preceding code to obtain a pointer to the IClassFactory interface is named CoGetClassObject—not CoGetClassFactory. In fact, CoCreateInstance passes the IID_IClassFactory interface identifier to CoGetClassObject to ensure that the interface pointer received can be used to call the IClassFactory::CreateInstance method.

A class object is a powerful abstraction that you can use to implement a custom activation interface instead of or in addition to IClassFactory. Therefore, while a class factory (an object that implements IClassFactory) can be called a class object, a class object is not necessarily a class factory. Since the IClassFactory::CreateInstance method is what really instantiates most coclasses, it, not CoCreateInstance, is the true analogue of the new operator. The declaration of the CreateInstance method shown below clearly illustrates that no parameter is provided to pass an argument to a class's constructor. By creating a class object with a custom activation interface (not IClassFactory), you can effectively redefine (or overload, in C++-speak) the new operation for a particular coclass:

HRESULT IClassFactory::CreateInstance(IUnknown* pUnkOuter, 
    REFIID riid, void** ppvObject);

Custom Activation Interfaces

By defining a custom activation interface and implementing that interface in a class object, you can pass extra arguments for the constructor, as expressed by the IPrimeFactory2 interface shown here:

interface IPrimeFactory : IUnknown
    HRESULT CreatePrime(
        [in] int starting_prime, 
        [out, retval] IPrime** ppPrime);

A class object implementing the IPrimeFactory interface works as well with the CoGetClassObject function called by the client as it does with the DllGetClassObject function exported by in-process components or the CoRegisterClassObject function called by executable components.3 The only requirement of both functions is that the class object implement IUnknown, not IClassFactory. However, because the CoCreateInstance helper function depends on the existence of the IClassFactory interface, it does not work with class objects that don't implement this interface. Thus, a client program will most likely have to access an object that implements a custom activation interface using the CoGetClassObject function, as shown here:

// Calling CoGetClassObject but requesting IPrimeFactory,
// not IClassFactory
IPrimeFactory* pPrimeFactory;
    IID_IPrimeFactory, (void**)&pPrimeFactory);

// Calling the IPrimeFactory::CreatePrime method and
// passing 7 to the constructor
IPrime* pPrime;
pPrimeFactory->CreatePrime(7, &pPrime);

// Now we have a Prime object.
int next_prime;
cout << next_prime << endl; // Displays 11

While this mechanism works well for C++ programs, a Visual Basic application will have a hard time instantiating the Prime object because its New keyword results in a call to CoCreateInstance. Since CoCreateInstance depends on finding an implementation of IClassFactory, it does not work with the Prime object. Calling CoGetClassObject directly from Visual Basic is theoretically possible, but this is not something most Visual Basic programmers would entertain.