一个非常简单的 COM 服务器,不含 ATL 或 MFC
一步一步地指导你用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; };
示例接口只公开了两个方法Square
和Cube
,都接受一个长指针作为参数。
第二步
现在我们需要提供我们定义的接口的实现,一种方法是创建一个从接口继承的新类,例如
// 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
的函数。 DllGetClassObject
为CmyInterface
创建一个类工厂,并返回对它的引用。当我们为进程内服务器调用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服务器,这可能会让你了解从哪里开始。你可能会在代码中发现错误,但这仅仅是因为其唯一目的是传达想法。你可以根据需要修改它。