65.9K
CodeProject 正在变化。 阅读更多。
Home

一个非常简单的 COM 服务器,不含 ATL 或 MFC

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.30/5 (17投票s)

2000 年 8 月 4 日

CPOL
viewsIcon

268281

downloadIcon

3392

一步一步地指导你用C++编写COM服务器,不使用MFC或ATL。

它是什么,为什么需要它!

这基本上是一个非常简单的DLL COM服务器,完全用C++实现,没有ATL或MFC的支持,并且非常容易编写。 以这种方式编写COM对象让我深入了解了COM如何处理进程内服务器以及如何创建类工厂。最终我得到的是一个简单的类框架,可用于实现相当基本的COM组件,例如Shell扩展等,发布此代码的全部目的是为了获得你们的一些反馈....

预期功能

为了编写你自己的COM对象,我们首先要做的是

第一步

编写一个接口头文件(项目中包含示例文件),并编写要实现的方法,如图所示。定义了对象的GUID,并为接口赋予了IID。我们需要在客户端使用它,如图所示

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    
                      );

我们还可以使用CLSIDFromProgId从注册表中获取CLSID,并将组件的ProgId传递给它。

 // 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;
};

示例接口只公开了两个方法SquareCube,都接受一个长指针作为参数。

第二步

现在我们需要提供我们定义的接口的实现,一种方法是创建一个从接口继承的新类,例如

// 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);

};

模板类InterfaceImpl<>提供了接口的引用计数的实现。在这里,我们可以从多个接口继承,以便单个COM组件可以实现多个接口。

第三步

编写QueryInterface和两个接口方法是我们在完成对象之前唯一剩下的事情。

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;
}

请注意,我们使用__uuidof(ImyInterface)来获取接口的接口IID。这是因为我们已经在第一步中将uuid与接口相关联。

最后一步

COM DLL必须导出一个名为DllGetClassObject的函数。 DllGetClassObjectCmyInterface创建一个类工厂,并返回对它的引用。当我们为进程内服务器调用CoCreateInstance时,COM通过调用DllGetClassObject为对象创建一个类工厂。类工厂有一个方法CreateInstance,用于创建对象并返回对它的引用。

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;
}

在这里,我们检查请求是否是针对由CLSID_Mine标识的对象的类工厂,如果不是,我们只需返回一个错误代码。

你可能想知道我们在哪里创建类CmyInterface的实际对象,这实际上是由CClassFactory<CmyInterface>的模板实例化处理的。这是CClassFatory的实际实现

// 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由COM调用来创建所需类型的对象,参数riid是所请求的接口的IID,如果对象支持,它会增加其引用计数并返回对其自身的引用。

关于代码

本文中的细节非常简短,并且省略了许多方面。它只是描述了从头开始实现COM服务器的基本细节。查看代码可以让你了解序列是如何工作的,如果你希望从头开始编写COM服务器,这可能会让你了解从哪里开始。你可能会在代码中发现错误,但这仅仅是因为其唯一目的是传达想法。你可以根据需要修改它。

© . All rights reserved.