Click here to Skip to main content
15,880,608 members
Articles / Programming Languages / C++
Article

A very simple COM server without ATL or MFC

Rate me:
Please Sign up or sign in to vote.
4.30/5 (18 votes)
3 Aug 2000CPOL 261.4K   3.4K   62   48
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, <BR>                              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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Pakistan Pakistan
Let a = b ....... (1)
a - b = a - b
a^2 - ab = a^2 - ab
a^2 - ab = a^2 - b^2 (from 1)
a (a - b) = (a + b) (a - b)
a = (a + b) ...... (2)

if a = 1
1 = (1 + 1) (from 1 & 2)
1 = 2 !!

Comments and Discussions

 
QuestionCreate COM Object not success. Pin
thiensy9-Apr-12 18:54
thiensy9-Apr-12 18:54 
QuestionRe: Create COM Object not success. Pin
.Shoaib10-Apr-12 6:12
.Shoaib10-Apr-12 6:12 
AnswerRe: Create COM Object not success. Pin
MotiP18-Apr-12 18:34
MotiP18-Apr-12 18:34 
QuestionCan i use this COM server in VB.net Pin
uks2k630-Nov-11 23:17
uks2k630-Nov-11 23:17 
AnswerRe: Can i use this COM server in VB.net Pin
.Shoaib1-Dec-11 0:27
.Shoaib1-Dec-11 0:27 
JokeWrong proof Pin
Damiendra Buwaneka3-Feb-09 1:18
Damiendra Buwaneka3-Feb-09 1:18 
GeneralRe: Wrong proof Pin
Member 1021183031-Jan-14 21:32
Member 1021183031-Jan-14 21:32 
GeneralOOT: Flaw in your signature Pin
Norman Sasono27-Jul-06 22:19
Norman Sasono27-Jul-06 22:19 
AnswerRe: OOT: Flaw in your signature Pin
.Shoaib8-Aug-06 19:59
.Shoaib8-Aug-06 19:59 
GeneralRe: OOT: Flaw in your signature Pin
rajbow19-Sep-06 1:26
rajbow19-Sep-06 1:26 
GeneralGreat Work, but some bug found. Pin
jung donghun7-Sep-05 19:39
jung donghun7-Sep-05 19:39 
GeneralGreat.... Pin
gavittl21-Aug-05 8:29
gavittl21-Aug-05 8:29 
GeneralMatlab COM Pin
dnqhung20-May-04 18:30
dnqhung20-May-04 18:30 
QuestionWhat kind of project is this Pin
tunafish243-Jan-04 18:42
tunafish243-Jan-04 18:42 
AnswerRe: What kind of project is this Pin
Prakash Nadar19-Jan-04 16:15
Prakash Nadar19-Jan-04 16:15 
Generalcould not initialise COM server Pin
Anonymous9-Dec-03 21:42
Anonymous9-Dec-03 21:42 
GeneralRe: could not initialise COM server Pin
songkiet8-Feb-04 20:34
songkiet8-Feb-04 20:34 
GeneralRe: could not initialise COM server Pin
Member 813948311-Aug-12 5:03
Member 813948311-Aug-12 5:03 
GeneralRe: could not initialise COM server Pin
Member 116368831-May-15 10:23
Member 116368831-May-15 10:23 
GeneralPlug-in for MS Outlook Pin
Atif Bashir30-Jul-03 16:31
Atif Bashir30-Jul-03 16:31 
GeneralRe: Plug-in for MS Outlook Pin
John M. Drescher30-Jul-03 16:40
John M. Drescher30-Jul-03 16:40 
GeneralSome food for COM lovers Pin
suhail197817-Jun-03 2:13
suhail197817-Jun-03 2:13 
hi all,
i have a really interesting and to the best of my knowledge a pretty
difficult project at hand.and i know that the best way for me to approach it is to ,apart from doing it myself ,let it be an open problem for all the COM experts out there.
I thought this is the correct forum to write so i am writing it here.in case somebody feels otherwise he can suggest me a better place,i shall be more then obliged.

The project(a part of our bigger project) is that we have to generate business logic at runtime.This involves generating a COM compliant dll at runtime.i have to go for COM dll and not static dlls .i have written a dll which takes the CLSID of the object,function name as string variable and the parameters in a safearray and loads the dll and calls the function.anyway that is done and out of my way now. now the big problem is to generate the
com dll.
there are basically two or three major sub problems:-
1) generating a minimal com server at runtime without business logic.
2) giving either a scripting language or a wonder gui to the user to generate
any business logic at runtime.
3) compile the source files thru appropriate compiler and then registering the dll.


the first two problems are pretty big but the third one is small.
i would suggest that we take them one at a time,obviosly starting from 1)

actually i don't know which way to start from:
-->try to call ATL application wizard at runtime
-->try to make raw com dll
i am open to any sort of suggestion or help .i hope that problem interests people as much as me.hope to have a long and fruitfull discussion on this

thanks in advance




with regards
Mohammad Suhail
Generalcalgonit Pin
calgonit3-Mar-03 23:40
calgonit3-Mar-03 23:40 
Questioncant work?? Pin
William.Zhang18-Dec-02 22:42
William.Zhang18-Dec-02 22:42 
AnswerRe: cant work?? Pin
Yogesh Kshatriya23-Jun-04 3:51
Yogesh Kshatriya23-Jun-04 3:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.