Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Single class object for multiple COM classes

0.00/5 (No votes)
2 Aug 2003 1  
Creation of multiple COM object types using a single Class Factory implementation

Introduction

The ClassFactory is the one of the major concepts in COM. COM provides various ways to control the creation process of the component. Proper understanding of the class factory will help programmers design COM components (i.e. coclass) and their corresponding class objects (i.e. class factories) in a better way. This article will cover the importance of class factories and an example which implements a single class object for multiple COM classes.

A class factory is a component whose main purpose is to create other components. A class object is also known as a class factory (class object and class factory are interchangeable) whose main purpose is to create the other components by implementing the standard interface called IClassFactory. Don�t get confused with the techy terms as different authors use different terminologies for explanation.

COM provides a generic way of creating the components. All the components are created in a similar manner. CoCreateInstance doesn�t provide the control on the creation process of the component. When CoCreateInstance returns, the component has already been created in memory and hence you have no control over the creation process of the component.

IClassFactory provides much more control over the creation process of the component, however it is much more confusing than CoCreateInstance. This can be considered as the price a programmer has to pay to get better control over the creation process.

CoCreateInstance provides a wrapper over the CoGetClassObject and CreateInstance methods of the IClassFactory interface. CoCreateInstance internally creates a class factory for the specified CLSID, gets the IClassFactory interface pointer and then creates the component by calling CreateInstance on the IClassFactory interface pointer. It then returns the requested interface pointer to the client by calling the QueryInterface method in the CreateInstance method of IClassFactory.

Object Creation Process

CoGetClassObject, which provides an interface pointer on a class object associated with a CLSID, needs an entry point in the DLL to create the component�s class factory. In most cases the class factory is implemented along with the component in the same DLL. The entry point is called DllGetClassObject, which creates the class factory. The client initiates the creation process by calling the CoGetClassObject function. The CoGetClassObject looks for a component in the system registry. If it finds the component, it loads the COM server, i.e. DLL, into a memory and then calls DllGetClassObject. The purpose of DllGetClassObject is to create the class object by calling the new operator for a class object. DllGetClassObject queries the class object for the IClassFactory interface, which is returned to the client.

The client, after receiving the IClassFactory interface pointer, calls the CreateInstance method. The IClassFactory::CreateInstance method calls the new operator to create the component. In addition to calling the new operator it also calls QueryInterface on the component for the iid interface.

As soon as the component is created and the interface (requested) pointer is returned to the client, the purpose of the class object (i.e. to create a component) is achieved and the client can release the class factory for that specific component (CLSID). The client can use the returned pointer on the component to call methods of that component.

Every COM DLL server should implement and export a function called DllGetClassObject. If a COM DLL doesn�t provides DllGetClassObject, then the call to CoGetClassObject or CoCreateInstance returns an error "Class Not Registered". DllGetClassObject is an entry point which basically creates the class factory for the specific class (CLSID). CoGetClassObject looks for a component in the registry and, if found, the component�s CLSID is passed as an argument to CoGetClassObject. If it finds the server, then it loads the COM DLL server (calls DllMain of the COM Server) that encapsulates that specific component. After loading the DLL, CoGetClassObject tries to get the address of DllGetClassObject (exported by the COM DLL) by calling the GetProcAddress function. If that fails then the COM SCM returns an error called "Class Not Registered" because there is no way to create a class factory for the requested component.

Sample code explanation

In the sample code, a single class object has been implemented for multiple COM classes (COM components). There could be a one-to-one mapping between class object and COM component supported. In that case, the DllGetClassObject should create a class object corresponding to the requested CLSID. This case is easy as we can have a multiple if cases in the DllGetClassObject and can call a new operator for the class object corresponding to a requested CLSID.

The purpose of the class object is to create another object (COM component), so the different class factory for different classes will cause the unnecessary duplication of code and makes code less readable. The COM server can be designed in such a way that the single class object should be able to support multiple COM components. This is what has been implemented in the sample code.

This has been achieved by the use of the helper function (creator function), which every COM class must support for its creation. There is a structure called FactoryInfo, which maps the creation function with the corresponding CLSID for all the COM classes that are exposed by the COM server.

struct FactoryInfo
{
    const CLSID *pCLSID;
    FPCOMPCREATOR pFunc;
};
When the client calls CoGetClassObject, the DllGetClassObject function is called with the requested CLSID as an argument. The DllGetClassObject function looks into the global array of FactoryInfo structures and traverses the array to fetch the address of the creator function, which is mapped to the requested CLSID. The class factory class stores this address in one of its data members, called pCreator, of FPCOMPCREATOR type. This stored address of the creation function in the CFactory class is used in the CreateInstance method of the IClassFactory interface. The address of the creator function is passed to the CFactory at the time of its creation by passing an argument of FPCOMPCREATOR type in the constructor of CFactory class.
// Code snippet.

// Traverse a list to find the helper function which corrosponds to

// the requested CLSID.

for (int iCount = 0; iCount < 2; iCount++)
{
    if (*gFactoryData[iCount].pCLSID == clsid)
    {
        break;
    }
}

CFactory *pFactory = new CFactory(gFactoryData[iCount].pFunc);
The above code is a part of the DlllGetClassObject function. This function traverses the global array of FactoryData structures and looks for the creation functions address corresponding to the requested CLSID. Once it gets the address of the creator function, it stops traversing the array and passes that address to the constructor of the CFactory class.

The CFactory stores this address of the creator function for further use. Once the IClassFactory interface on the class object is returned to the client the client can call the CreateInstance method of the IClassFactory to create an instance of the COM class. The CreateInstance call is the place where the COM component is created and the requested interface is returned on that newly created COM components instance. The creator function, whose address has been stored in CFactory�s (class object) data member, is called in the CreateInstance method and the requested interface is returned to the client.

//// Code snippet.

typedef HRESULT (*FPCOMPCREATOR) (const IID&, void**);

class CFactory : public IClassFactory
{
public:
    // Rest of the code has been removed from here to make it readable.

    CFactory(FPCOMPCREATOR);
    ~CFactory();

private:
    /* This is to store the address of the creator function of the
     * COM component with the requested CLSID.
     */
    FPCOMPCREATOR pCreator;
    long m_cRef;
};
This is a call to the creator function in the CreateInstance method of the CFactory class.
hResult = (*pCreator)(iid,ppv);
FPCOMPCREATOR is a synonym for a "pointer to a function which takes const IID & and void** as an argument and returns an HRESULT".

The code has been commented properly to make it self-explanatory. The single class object for multiple COM classes will help you grasp the Class Factory concept in COM.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here