开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 2217|回复: 1
收起左侧

[其它] 用C++语言编写COM组件

[复制链接]

发表于 2013-2-18 12:39:29 | 显示全部楼层 |阅读模式   江苏省泰州市
本文提供一个完全用C++实现的进程内(DLL)COM服务器,不要ATL或MFC提供任何支持。用这种方式编写COM对象可以让你深入地洞察到COM处理进程内服务器的方法以及COM是如何创建类工厂的。利用本文提供的这个简单框架你可以实现很基本的COM组件,如外壳扩展(Shell Extensions)等。


本文提供一个完全用C++实现的进程内(DLL)COM服务器,不要ATL或MFC提供任何支持。用这种方式编写COM对象可以让你深入地洞察到COM处理进程内服务器的方法以及COM是如何创建类工厂的。利用本文提供的这个简单框架你可以实现很基本的COM组件,如外壳扩展(Shell Extensions)等。
以下是用本文所说的方式编写自己的COM对象要经过的步骤:
第一步:写一个头文件,这个头文件包含以下内容:
1、 包含文件comdef.h:#include <comdef.h>。
2、 定义COM服务器的GUID。



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

3、 给出接口的IID以及这个接口要实现的方法定义。到时客户端会用到这个接口的IID和接口的方法。



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

客户端使用此接口:



    HRESULT hr;
  • ImyInterface *pmine=(0);  
    hr = CoCreateInstance(CLSID_Mine, // COM 服务器的CLSID
  • NULL, //不支持聚合  
    CLSCTX_INPROC_SERVER, // 是个DLL
  • __uuidof(ImyInterface), // 接口的IID  
    (void**)&pmine
  • );

还有一种方法可以从注册表中获得COM对象的CLSID,就是调用CLSIDFromProgId()函数,不过必须把组件的ProgId传递给这个函数。
第二步:必须为所定义的接口提供实现,本文用的方法是创建一个从接口继承的新类:



    // 这个类实现单接口ImyInterface ...
  • //   
    //
  • class CmyInterface : public CComBase<> ,   
    public InterfaceImpl<ImyInterface>
  • {  
    public:
  • CmyInterface();  
    virtual ~CmyInterface();
  • // 我们必须要为QueryInterface 编写代码  
    STDMETHOD(QueryInterface)(REFIID riid,LPVOID *ppv);
  • // ImyInterface 接口方法  
    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)))  
    {
  • // 因为我们从ImyInterface继承,所以要进行强制类型转换  
    *ppv = (ImyInterface *) this;

  • _AddRef(); // 这个方法从某个基类继承而来
  • 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 组件的DLLs必须输出一个叫DllGetClassObject的函数。由这个函数为CmyInterface创建类工厂并返回一个对它的引用。然后我们调用CoCreateInstance为进程内COM创建类工厂,接着调用DllGetClassObject。这个类工厂有一个方法是CreateInstance,由这个方法创建对象并返回对它的引用。



    STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
  • {  
    *ppvOut = NULL;
  • if (IsEqualIID(rclsid, CLSID_Mine))  
    {
  • // 为CmyInterface类声明类工厂  
    CClassFactory<CmyInterface>
  • *pcf = new CClassFactory<CmyInterface>;   
    return pcf->QueryInterface(riid,ppvOut);
  • }  
    return CLASS_E_CLASSNOTAVAILABLE;
  • }

在此我们要检查所请求的CLSID是不是CLSID_Mine,如果不是则返回一个错误代码。
你可能会问在哪里创建实际的CmyInterface类对象,实际上这是由CClassFactory<CmyInterface>的模板实例来处理的。以下是CClassFatory的实现:



    // CSingleCreator 用于单实例类工厂,这个类为多个CreateObject请求返回相同的对象指针..  
  • 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;
  • };  
    // CMultiCreator 用于常用类工厂,这个类为每一个CreateObject请求返回新的对象指针..
  • template<class comObj>  
    class CMultiCreator
  • {  
    protected:
  • CMultiCreator():m_pObj(0) {};  
    comObj *CreateObject()
  • {  
    return new comObj;
  • }  
    comObj * m_pObj;
  • };  
    //ClassFactory类实现
  • // MultiCreator是缺省的类工厂创建者  
    //这个类实现了接口IclasFactory......

  • 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 在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; } // 未实现
  • };

COM调用CreateInstance创建请求的对象,参数riid指的是所请求的接口IID,如果这个对象支持这个接口,则增加它的引用计数并返回对自身的引用。
关于代码:本文所提出的方法是如何用纯粹的C++编写COM组件的一个大概念。很多方面的细节都省略了。从本文的文字和代码中可以看出用纯C++编写COM组件需要做些什么工作,如果你要用这种方法编写COM组件的话,这些代码只能是抛砖引玉,具体的实现可以在此基础上往下做.


结帖率:100% (16/16)
发表于 2013-2-18 12:42:46 | 显示全部楼层   四川省成都市
沙发。。。。。。。。。沙发
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表