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

A very simple COM server without ATL or MFC

0.00/5 (No votes)
3 Aug 2000 1  
A step by step guide to write a COM server using C++ without MFC or ATL.

What is it and Why is it!

This is basically a very simple DLL COM server implemented totally in C++ with no support from ATL or MFC and is very easy to write. Writing a COM object this way gave me an insight into the way inproc servers are handled by COM and how Class Factories are created. What I ended up with was a simple framework of classes that can be used to implement fairly basic COM components like Shell Extensions etc, with the whole purpose of publishing this code being to get some feed back from you people....

What to expect

In order to write your own COM object the first thing that we have to do is:

Step One

Write an Interface header file (sample file included in the project) and write methods that you want to implement as shown. A GUID for the object is defined and the interface is given an IID. We need to use this from the client side as shown:

HRESULT hr;
ImyInterface *pmine=(0);
hr = CoCreateInstance(CLSID_Mine,               // CLSID of COM server

                      NULL,          
                      CLSCTX_INPROC_SERVER,     // it is a DLL 

                      __uuidof(ImyInterface),   // the interface IID

                      (void**)&pmine    
                      );

We can also get the CLSID from the registry using CLSIDFromProgId passing it the ProgId of the component.

 // Interface.h

 // GUID of our COM server

 _declspec(selectany) GUID CLSID_Mine = { 0xdc186800,  0x657f,  0x11d4, 
          {      
          0xb0, 
          0xb5,
          0x0,  
          0x50,  
          0xba,  
          0xbf,  
          0xc9,  
          0x4   
          }
        };

// interface definition

// for sample COM server just replace the uuid of interface and its name

// and add new method prototypes here ..

// 

interface __declspec(uuid("F614FB00-6702-11d4-B0B7-0050BABFC904")) 
         ImyInterface : public IUnknown
{ 
  STDMETHOD(Square)(long *pVal)PURE;
  STDMETHOD(Cube)(long *pVal)PURE;
};

The sample interface just exposes two methods Square and Cube both taking a long pointer as parameter.

Step Two

Now we need to provide an implementation of the interface that we have defined, one way to do it is by creating a new class that inherits from interface like:

// this class implements a single interface ImyInterface ...

// 

class CmyInterface : public CComBase<> , 
                     public InterfaceImpl<ImyInterface> 
{
public:
  CmyInterface();
  virtual ~CmyInterface();

  // we however need to write code for queryinterface 

  STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv);

  // ImyInterface methods

  STDMETHOD(Square)(long *pVal);
  STDMETHOD(Cube)(long *pVal);

};

The template class InterfaceImpl<> provides the implementation of reference counting for the interfaces. Here we can inherit from multiple interfaces so that a single COM component can implement more than one interface.

Step Three

Writing Queryinterface and the two interface methods is the only thing left before we finish with the object.

STDMETHODIMP CmyInterface::QueryInterface(REFIID riid,LPVOID *ppv)
{
  *ppv = NULL;
  if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,__uuidof(ImyInterface)))
  {
    // since we derive publically from ImyInterface this is a valid typecast

    *ppv = (ImyInterface *) this;  
    
    _AddRef();    // this method is inherited from a base class

    return S_OK;
  }
  return E_NOINTERFACE;
}

STDMETHODIMP CmyInterface::Square(long *pVal)
{
  long value = *pVal;
  *pVal = value * value;
  return S_OK;
}

STDMETHODIMP CmyInterface::Cube(long *pVal)
{
  long value = *pVal;
  *pVal = value * value * value;
  return S_OK;
}

Note that, we are using __uuidof(ImyInterface) to get the interface IID of the interface. This is because we already have associated a uuid with the interface in step One.

Final Step

COM DLLs have to export a function called DllGetClassObject. DllGetClassObject creates a Class Factory for CmyInterface and returns a reference to it. When we call CoCreateInstance for an inproc server, COM creates a class factory for the object by calling DllGetClassObject. The class factory has a method CreateInstance that creates the object and returns references to it.

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
{
    *ppvOut = NULL;
    if (IsEqualIID(rclsid, CLSID_Mine))
    {
       // declare a classfactory for CmyInterface class 

       CClassFactory<CmyInterface> 
       *pcf = new CClassFactory<CmyInterface>; 
       return pcf->QueryInterface(riid,ppvOut);
    }
    return CLASS_E_CLASSNOTAVAILABLE;
}

Here we check to see, if the request is for Class Factory of objects identified by CLSID_Mine and if not, we simply return an error code.

You might be asking where are we creating the actual objects of class CmyInterface , this infact is handled by the template instantiation of CClassFactory<CmyInterface>. Here is what the actual implementation of CClassFatory is:

// Creator class for Singleton class factories this class returns the 

// pointer to the same object for multiple CreateObject requests ..

 
template<class comObj>
class CSingleCreator
{
protected:
  CSingleCreator():m_pObj(0) {};

  comObj *CreateObject()
  {
    if(!m_pObj)
    {
      m_pObj = new comObj;
    }
    return m_pObj;
  }
  comObj * m_pObj;
};

// Creator class for normal class factories this class returns 

// the pointer to a new object for each CreateObject request ..

 
template<class comObj>
class CMultiCreator
{
protected:
  CMultiCreator():m_pObj(0) {};
  comObj *CreateObject()
  {
    return new comObj;
  }
  comObj * m_pObj;
};

// ClassFactory implementation using the classes all around us 

// now the default creator class for classfactory is MultiCreator 

// this class implements IClasFactory interface ....

 
class CClassFactory  : public CComBase<>,
                       public InterfaceImpl<IClassFactory>,
                       public creatorClass 
{
public:
  CClassFactory() {};
  virtual ~CClassFactory() {};

  STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv)
  {
    *ppv = NULL;
    if(IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IClassFactory))
    {
      *ppv = (IClassFactory *) this;
      _AddRef();   
      return S_OK;
    }
    return E_NOINTERFACE;
  }

  STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, 
LPVOID *ppvObj) { *ppvObj = NULL; if (pUnkOuter) return CLASS_E_NOAGGREGATION; m_pObj = CreateObject(); // m_pObj is defined in creatorClass if (!m_pObj) return E_OUTOFMEMORY; HRESULT hr = m_pObj->QueryInterface(riid, ppvObj); if(hr != S_OK) { delete m_pObj; } return hr; } STDMETHODIMP LockServer(BOOL) { return S_OK; } // not implemented };

CreateInstance is called by COM to create an object of the required type, parameter riid is the IID of the interface that is requested and if the object supports, it increments its reference count and returns a reference to itself.

About the Code

The details in this articles are the very brief and many aspects have been omitted. It just describes the bare details of how to implement a COM server from scratch. Having a look at the code gives you an idea of how the sequence works and if you are looking to write a COM server from scratch this may give you an idea of where to start. You may expect bugs in the code but this is because the sole purpose is to convey the ideas. You can modify it to suit your needs if you like.

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