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.
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.
typedef HRESULT (*FPCOMPCREATOR) (const IID&, void**);
class CFactory : public IClassFactory
{
public:
CFactory(FPCOMPCREATOR);
~CFactory();
private:
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.