博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
brew组件实现原理
阅读量:5881 次
发布时间:2019-06-19

本文共 6940 字,大约阅读时间需要 23 分钟。

  前文提到我的FM收音机是通过com+app来实现的。这里的com是brew的组件,和微软的com组件是不一样的。在这里我不想讨论微软的com组件和brew的组件有什么不一样,而是想详细的分析一下brew的组件原理。(因为我对微软的com组件也不是很熟悉,之前做过windows mobile的开发,但是没做多久就换平台了。。。)就让我们拿来做为例子来深入的了解brew组件的原理和实现吧!

  拿FM收音机的组件来说,头文件中定义如下(去掉了一些函数指针):

  typedef struct IFMRadio IFMRadio;

  #define INHERIT_IFMRadio(iname) \

  INHERIT_IQueryInterface(iname); \

  int (*Start)(iname *po);\

  int (*Stop)(iname *po)

  AEEINTERFACE(IFMRadio)

  {

    INHERIT_IFMRadio(IFMRadio); 

  };

 

  struct IFMRadio

  {

    const AEEVTBL(IFMRadio)* pvt;

    void* pData;

  };

 

  #define IFMRadio_AddRef(pThis) \

  (pThis)->pvt->AddRef((pThis))

 

  #define IFMRadio_Release(pThis) \

  (pThis)->pvt->Release((pThis))

 

  #define IFMRadio_QueryInterface(pThis, ClsID, ppInterface) \

  (pThis)->pvt->QueryInterface((pThis), (ClsID), (ppInterface))

 

  #define IFMRadio_Start(pThis) \

  (pThis)->pvt->Start((pThis))

 

  #define IFMRadio_Stop(pThis) \

  (pThis)->pvt->Stop((pThis))

  我们首先看第一宏INHERIT_IQueryInterface,它在AEEQueryInterface.h中定义如下
  #define INHERIT_IQueryInterface INHERIT_IQI
  而INHERIT_IQI也是一个宏,它在AEEIQI.h中定义为:

  #define INHERIT_IQI(iname) \

  uint32 (*AddRef)(iname*);\

  uint32 (*Release)(iname*);\

  int    (*QueryInterface)(iname *, AEEIID, void **)

  实际上它是三个函数指针,所以

  #define INHERIT_IFMRadio(iname) \

  INHERIT_IQueryInterface(iname); \

  int (*Start)(iname *po);\

  int (*Stop)(iname *po)

  进行宏替换后就成了

  #define INHERIT_IFMRadio(iname) \

  uint32 (*AddRef)(iname*);\

  uint32 (*Release)(iname*);\

  int    (*QueryInterface)(iname *, AEEIID, void **);\

  int (*Start)(iname *po);\
  int (*Stop)(iname *po) 

  接下来看AEEINTERFACE的宏定义(它在AEEInterface.h文件中):

  #define AEEINTERFACE(iname) \

  typedef struct AEEVTBL(iname) AEEVTBL(iname); \

  struct AEEVTBL(iname)

再对它进行宏替换,就成了如下形式:

  typedef struct AEEVTBL(IFMRadio) AEEVTBL(IFMRadio); \

  struct AEEVTBL(IFMRadio)

  {

    INHERIT_IFMRadio(IFMRadio);

  };

  对INHERIT_IFMRadio进行宏替换后:

  typedef struct AEEVTBL(IFMRadio) AEEVTBL(IFMRadio); \

  struct AEEVTBL(IFMRadio)

  {

    uint32 (*AddRef)(IFMRadio*);\

    uint32 (*Release)(IFMRadio*);\

    int    (*QueryInterface)(IFMRadio *, AEEIID, void **);\

    int (*Start)(IFMRadio *po);\

    int (*Stop)(IFMRadio *po) 

  };

  这里还有一个AEEVTBL宏,它也在AEEInterface.h中定义了:

  #define AEEVTBL(iname) iname##Vtbl

  于是对这个宏进行替换就成了下面的形式:

  typedef struct IFMRadioVtbl IFMRadioVtbl; \

  struct IFMRadioVtbl

  {

    uint32 (*AddRef)(IFMRadio*);\

    uint32 (*Release)(IFMRadio*);\

    int    (*QueryInterface)(IFMRadio *, AEEIID, void **);\

    int (*Start)(IFMRadio *po);\

    int (*Stop)(IFMRadio *po)

  };

  所以的宏替换之后,头文件变成了如下形式:

  typedef struct IFMRadio IFMRadio;

  typedef struct IFMRadioVtbl IFMRadioVtbl; \
  struct IFMRadioVtbl
  {

    uint32 (*AddRef)(IFMRadio*);\

    uint32 (*Release)(IFMRadio*);\

    int    (*QueryInterface)(IFMRadio *, AEEIID, void **);\

    int (*Start)(IFMRadio *po);\

    int (*Stop)(IFMRadio *po)

  };

  struct IFMRadio

  {

    const IFMRadioVtbl* pvt;

    void* pData;

  };

  结构体IFMRadio实际上是一个结构体指针IFMRadioVtbl*和一个空指针,而这个结构体IFMRadioVtbl是一些函数指针。
  再来看FM收音机的源文件(略去了一些具体的代码):

  typedef struct

  {

  uint32 nRefs;

  IShell* pIShell; 
  IFMRadio myIFMRadio;

  } CFMRadio;

  static const AEEVTBL(IFMRadio) gIFMRadioFuncs =

  {

    CFMRadio_IBase_AddRef,

    CFMRadio_IBase_Release,
    CFMRadio_IBase_QueryInterface,
    CFMRadio_IFMRadio_Start,
    CFMRadio_IFMRadio_Stop,

  };

  static  int CFMRadio_IFMRadio_Start(IFMRadio *pIFMRadio)

  {

    int result = SUCCESS;

    //实现的代码...

    return result;

  };

  static int CFMRadio_IFMRadio_Stop(IFMRadio *pIFMRadio)

  {

    int result = SUCCESS;

    //实现的代码...

    return result;

  }

  static int CFMRadio_IBase_QueryInterface(IFMRadio* pIFMRadio, AEECLSID ClsID, void** ppInterface)

  { 

    if (ClsID == AEECLSID_QUERYINTERFACE || ClsID == SYS_CLSID_IFMRADIO)

    {

      IBASE_COPY_IPTR(*ppInterface, pIFMRadio);

      return SUCCESS;

    }

    return ECLASSNOTSUPPORT;

  }

  int CFMRadio_New(IShell* pIShell, AEECLSID ClsID, void** ppInterface)

  {

    int result = SUCCESS;

    IFMRadio* pIFMRadio = NULL;

    CFMRadio* pThis = NULL;

    if (pIShell == NULL || ppInterface == NULL)

    {

      return EBADPARM;

    }

 
    if (ClsID != SYS_CLSID_IFMRADIO)
    {

      return EUNSUPPORTED;

    }
 
    pThis = CFMRadio_Constructor(pIShell); 
    if (pThis == NULL)
    {
       return ENOMEMORY;
    }

    pIFMRadio = &pThis->myIFMRadio;

    result = IFMRadio_QueryInterface(pIFMRadio, SYS_CLSID_IFMRADIO, ppInterface);
    if (SYS_FAILURE(result))
    {

      CFMRadio_Destructor(pThis);

    }

    return result;

  }

  static CFMRadio* CFMRadio_Constructor(IShell* pIShell)

  {

    CFMRadio* pThis = NULL;

    pThis = (CFMRadio*)UMPAlloc(sizeof(CFMRadio));

    if (pThis == NULL)

    {

      return NULL;

    }

 

    pThis->nRefs = 0;

    IBASE_COPY_IPTR(pThis->pIShell, pIShell);
    pThis->myIFMRadio.pvt = &gIFMRadioFuncs;
    pThis->myIFMRadio.pData = (void*)pThis;

    if (SYS_FAILURE(CFMRadio_Initialise(pThis)))

    {
      CFMRadio_Destructor(pThis);
      pThis = NULL;
    }
    return pThis;

  }

  我们要调用组件中的接口函数首先要调用ISHELL_CreateInstance创建这个组件的实例,创建成功后才能调用这个组件的接口函数,调用ISHELL_CreateInstance这个函数会传递一个classid,通过这个classid会调用对应的New函数(这个classid和相对应的New函数是通过一个静态类的数组关联起来的),CFMRadio_New函数会调用CFMRadio_Constructor函数,CFMRadio_Constructor函数首先为组件分配内存,接着是两个赋值语句:
  pThis->myIFMRadio.pvt = &gIFMRadioFuncs;
  pThis->myIFMRadio.pData = (void*)pThis;
  通过前面我们可以看到gIFMRadioFuncs的定义,宏替换后就是下面的形式:

  static const IFMRadioVtbl gIFMRadioFuncs =

  {

    CFMRadio_IBase_AddRef,
    CFMRadio_IBase_Release,
    CFMRadio_IBase_QueryInterface,
    CFMRadio_IFMRadio_Start,
    CFMRadio_IFMRadio_Stop,

  };

  它定义一个全局的结构体,并进行了初始化,于是IFMRadioVtbl中的函数指针就被初始化为这些函数名,

  uint32 (*AddRef)(IFMRadio*); 对应CFMRadio_IBase_AddRef,

  uint32 (*Release)(IFMRadio*);对应CFMRadio_IBase_Release,

  int    (*QueryInterface)(IFMRadio *, AEEIID, void **);对应CFMRadio_IFMRadio_Start,

  int (*Start)(IFMRadio *po);对应CFMRadio_IFMRadio_Start,

  int (*Stop)(IFMRadio *po)对应CFMRadio_IFMRadio_Stop,

  而下面一句则是把pThis赋给了结构体IFMRadio中的pData,接着我们来看它是app是如何进行调用的,app首先定义一个IFMRadio的指针*pFMRadio;接着调用ISHELL_CreateInstance创建这个组件的实例,并将第三个参数设置为(void**)&pFMRadio传递进去,这时会调用CFMRadio_New函数,它也接收(void**)&pFMRadio作为参数,它首先调用CFMRadio_Constructor函数对myIFMRadio结构体进行初始化,接着调用接口函数IFMRadio_QueryInterface接口函数,它实际上利用IBASE_COPY_IPTR(*ppInterface, pIFMRadio)对pIFMRadio指针进行初始化,实际上是将&pThis->myIFMRadio赋值给了pIFMRadio,如果我们调用IFMRadio_Start(pIFMRadio),通过前面的宏定义,

  #define IFMRadio_Start(pThis) \

  (pThis)->pvt->Start((pThis))

  实际上是(pIFMRadio)->pvt->Start(pIFMRadio),而pIFMRadio又被赋值为&pThis->myIFMRadio,所以调用变成了

  (&pThis->myIFMRadio)->pvt->Start(&pThis->myIFMRadio)

  而在CFMRadio_Constructor中pThis->myIFMRadio.pvt又被初始化为&gIFMRadioFuncs,其中start函数指针被初始化为CFMRadio_IFMRadio_Start,所以最后IFMRadio_Start实际上调用的是CFMRadio_IFMRadio_Start函数。有的地方把这叫做类的扩展,而我们部门叫做组件。根据它的实现原理,我觉得叫做组件更确切一些(虽然它和微软的com组件不一样)。组件的实现有多种方式,实际上是大同小异的,一般会有一些宏不太一样,这些宏在AEE.h和AEEInterface.h等一些头文件都会有定义,进行宏替换后的结构基本上都是一样的。

转载于:https://www.cnblogs.com/rager/archive/2010/08/15/1800235.html

你可能感兴趣的文章
劣质代码评析——猜数字问题(上)
查看>>
纸上谈兵: 栈 (stack)
查看>>
Windows phone8 基础篇(三) 常用控件开发
查看>>
Oracle学习笔记之五,Oracle 11g的PL/SQL入门
查看>>
大叔手记(3):Windows Silverlight/Phone7/Mango开发学习系列教程
查看>>
考拉消息中心消息盒子处理重构(策略模式)
查看>>
so easy 前端实现多语言
查看>>
【追光者系列】HikariCP源码分析之ConcurrentBag&J.U.C SynchronousQueue、CopyOnWriteArrayList...
查看>>
在navicat中如何新建连接数据库
查看>>
canvas系列教程05-柱状图项目3
查看>>
css绘制几何图形
查看>>
HTML标签
查看>>
理解JS中的Event Loop机制
查看>>
转载:字符编码笔记:ASCII,Unicode和UTF 8
查看>>
修复看不懂的 Console Log
查看>>
Android跨进程通信 AIDL使用
查看>>
ajax常见面试题
查看>>
结合kmp算法的匹配动画浅析其基本思想
查看>>
vue进行wepack打包执行npm run build出现错误
查看>>
【d3.js v4基础】过渡transition
查看>>