zoukankan      html  css  js  c++  java
  • 《COM原理与应用》学习笔记二——COM对象和COM接口的实现

      COM对象是给用户提供服务的封装的实体。这个应该和C++中类的对象理解起来是相似的。但是有时候也把COM对象当作提供服务的那个类。COM对象也对数据进行了封装,然后也提供了接口。不过和类还是有一些不一样的。类中的数据可以申明为public,然后让用户能够直接访问这些数据成员。但是用户不能对COM对象的数据进行直接访问,只能通过接口(如果有提供这种接口的话)来对数据进行间接的访问。一般COM接口指的是一组提供服务的接口,刚开始看这个定义很不习惯。因为C++中根本没有接口的概念,但是像Java这些语言就有接口的概念。关于COM接口可以按照Java的接口来理解(多懂几门语言真心是好事啊)。C++中的定义方式一般是用抽象类来实现,其中所有的函数全都是纯需函数,全部交给子类来实现。如某个接口的定义如下:

      

    1 struct ICalc
    2 {
    3     virtual long __stdcall add(long a, long b) = 0;
    4     virtual long __stdcall minus(long a, long b) = 0;
    5     virtual long __stdcall times(long a, long b) = 0;
    6     virtual long __stdcall devide(long a, long b) = 0;
    7 };

      客户使用COM组件的时候,是不知道COM组件的确切对象的。所以COM组件需要注册注册表。但是客户还是需要一点什么标识来访问COM组件,使用COM对象,第一个直接的想法就是取个名字。但是会出现这种情况,A公司做个插件叫做Calculator,B公司做个插件也可以叫做Calculator,甚至家里的旺财写个插件都可以叫做Calculator。所以这个方法不是很好。为了解决这个重复的问题。微软决定使用GUID来对插件进行标识。GUID全称是Globally Unique Identifier,是一个128位的随机数。大家都随机,不怕随到一样的数吗?恩,确实有那个可能,但是这个概率太低了。理论上,如果一台计算机每秒产生10 000 000个GUID,则可以保证3240年不重复(当然是概率意义上,怎么算的了,我也不清楚啦)。反正GUID重复的概率是非常低的,可以放心使用。每个COM对象都有一个GUID来进行标识,这个GUID一般叫做CLSID。每一个COM接口也有一个GUID来进行标识,这个GUID一般叫做IID。在C++中,GUID定义如下:

    1 typedef struct _GUID
    2 {
    3     DWORD Data1;
    4     WORD  Data2;
    5     WORD  Data3;
    6     BYTE  Data4[8];
    7 } GUID;

      按照COM规范,只要用户获得了一个COM对象的接口,那么用户可以通过它获取到该COM对象的其他接口(如果有的话)。一个用户在使用COM对象以前,用户需要建立COM对象(我觉得COM对象这个叫法真心的很讨厌,书上说COM对象既指那个提供服务的类,也指根据这个类实例化的对象)。建立COM对象当然是要分配一些资源的,在用完这些资源以后当然也是需要释放的,所以每一个COM对象都有一个计数。计数值为0的时候,那么对象就销毁这个COM对象。当有另外一个指针获得这个COM对象的话,那么计数就会增加一个,有一个指针释放了对这个COM对象的控制了,那么计数就减一。这种计数的方式类似于智能指针。但是COM对象的计数也没有智能指针那么智能,有时还是需要手动来释放的,所以用户承担着释放资源的重任。基于以上的说法,我们就需要一个获取其他所有接口的接口,增加计数的接口和减少计数的接口。COM提供的IUnknown接口已经提供了这三个接口(注意:COM接口一般指的是一组接口,而不是一个接口)。实际上所有的COM接口都必须从IUnknown继承而来。IUnknown提供了QueryInterface、AddRef和Release这三个接口。按C++的定义如下,但是实际定义的写法比较麻烦,这里只是比较简单的写法。

    1 class IUnknown
    2 {
    3 public:
    4     virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv) =0;
    5     virtual ULONG __stdcall AddRef() =0;
    6     virtual ULONG __stdcall Release() = 0;
    7 }

    IUnknown是一个接口当然也有一个IID,IUnknown的IID为IID_IUnknown=00000000-0000-0000-C000-000000000046。然后剩下就是用户自己定义接口了。用户也可以定义多个接口。最后提供服务的类集成这些接口就组成了一个能够被用来创建COM对象的类了。比如下面的ICalc是一个接口,CCalc就是一个实现服务的类。

     1 class ICalc : public IUnknown
     2 {
     3 public:
     4     virtual long __stdcall add(long a, long b) = 0;
     5     virtual long __stdcall minus(long a, long b) = 0;
     6     virtual long __stdcall times(long a, long b) = 0;
     7     virtual long __stdcall devide(long a, long b) = 0;
     8 };
     9 
    10 class CCalc : public ICalc
    11 {
    12 public:
    13     CCalc();
    14     ~CCalc() = default;
    15 
    16 public:
    17     //IUnknown interface:
    18     virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv) override;
    19     virtual ULONG __stdcall AddRef() override;
    20     virtual ULONG __stdcall Release() override;
    21 
    22     //ICalc interface:
    23     virtual long __stdcall add(long a, long b) override;
    24     virtual long __stdcall minus(long a, long b) override;
    25     virtual long __stdcall times(long a, long b) override;
    26     virtual long __stdcall devide(long a, long b) override;
    27 
    28 private:
    29     long m_lRef;
    30 };

    其中override是C++11标准所提供的关键字。
      这一篇博客就先写到这吧,后面还有需要继续写的,但是现在要回寝室了,后续的明天或者后天继续写。

    PS:博主也是一个菜鸟,最近才开始写技术博客,如果各位发现了什么错误,欢迎拍砖,指出错误。

  • 相关阅读:
    Coding Souls团队---电梯演讲
    第一次团队会议总结-NABCD分析
    软件工程团队项目介绍
    python进行四舍五入
    python列表元素两两比较
    Linux常用命令
    谷歌日历的正确用法--在谷歌日历中添加农历、天气、中国节假日
    Nose框架的安装
    python中staticmethod装饰器的作用
    python 3.x与python 2.7.x在语法上的区别
  • 原文地址:https://www.cnblogs.com/DennisXie/p/3961648.html
Copyright © 2011-2022 走看看