读《基于COM思想实现AEEINTERFACE》有感

(备注:此文提到的AEEINTERFACE是跟BREW平台相关的,原文可以在手机之家——开发联盟里头找到)

 

仔细阅读了Qinix的《基于COM思想实现AEEINTERFACE》,彻底被作者折服,作者用极其简单的方法给大家阐述了AEEINTERFACE的实现方式。
不过由于作者在文中,及其文后附带的代码中,对于BREW平台所自带的宏,没有展开,给阅读代码带来了一定困难,于是我把它们展开,在这个过程中,又遇到了一些问题,经过仔细思考,得到一些不成熟的答案,拿出来跟大家分享、讨论一下。

BREW平台里头有一些比较复杂的宏,关于文中所遇到的宏,列表如下:

#define AEEINTERFACE(iname) /
           typedef struct AEEVTBL(iname) AEEVTBL(iname); /
           struct AEEVTBL(iname)

#define AEEVTBL(iname) iname##Vtbl

#define INHERIT_IQueryInterface(iname) /
   INHERIT_IBase(iname); /
   int (*QueryInterface)(iname *, AEECLSID, void **)

#define INHERIT_IBase(iname) /
  uint32  (*AddRef)         (iname*);/
  uint32  (*Release)        (iname*)


因此,关于IICAT的定义,可以分解为:
AEEINTERFACE(IICat)
{
    INHERIT_IQueryInterface(IICat);
    void (*IgnoreMaster)(IICat* po);
};

-->>

typedef struct IICatVtbl IICatVtbl;
struct IICatVtbl
{
 /** INHERIT_IQueryInterface **/

 /** INHERIT_IBase **/
 uint32  (*AddRef) (IICat*);
 uint32  (*Release) (IICat*);
 /** end-INHERIT_IBase **/

 int (*QueryInterface)(IICat *, AEECLSID, void **)
 /** end-INHERIT_IQueryInterface **/

    void (*IgnoreMaster)(IICat* po);
};

关于CICat的定义,可以分解为:
struct CICat
{
    const AEEVTBL(IICat)       *pvt;
    uint32                      m_uRef;
    IShell                     *m_pIShell;  
    ICat*                       m_pCat;             
};

-->>

struct CICat
{
    const IICatVtbl  *pvt;
    uint32                      m_uRef;
    IShell                      *m_pIShell;  
    ICat*                       m_pCat;             
};

关于接口函数:
#define AEEGETPVTBL(p,iname)  (*((AEEVTBL(iname) **)((void *)p)))

#define IICAT_AddRef(p)                                   AEEGETPVTBL(p,IICat)->AddRef((p))
#define IICAT_Release(p)                                  AEEGETPVTBL(p,IICat)->Release((p))
#define IICAT_QueryInterface(p,i,o)                       AEEGETPVTBL(p,IICat)->QueryInterface((p),(i),(o))
#define IICAT_IgnoreMaster(p)                             AEEGETPVTBL(p,IICat)->IgnoreMaster((p)) 

例如:IICAT_AddRef(IICAT)
就是 (*((IICATVtbl **)((void *)IICAT)))->AddRef((IICAT))

关键的几个结构体:CCAT, CICat

当程序运行的时候,ISHELL_CreateInstance会根据CLSID调用IICat_New函数,该函数创建CICat结构体对象,在创建的过程中,首先创建类CCAT的实例,指针赋值给m_pCat,然后会给pvt指针赋值(也就是那个static const gvtIICat),最后分别给m_uRef和m_pIShell赋值。

通过以上的步骤,你分别拥有了CCAT和CICat的实例,那么你就可以通过调用那些以IICAT_开头的函数来完成你需要的功能了,不过接口函数中肯定都回有CICat的实例作为参数,因为你实际调用就是CICat结构体中的函数。

比如说你调用IICAT_IgnoreMaster(p)函数,它实际上是调用的CICat->IgnoreMaster(p),它又实际上是调用的CCAT->IgnoreMaster(p)。

在阅读的时候遇到过两个疑问,现在得到一些不成熟的解释:

1、文中对类、结构体都进行了精巧的设计,那么可以说,这是C和C++混合在一起的,开始一直不明白,这样的代码,如何在纯C的环境下编译通过,因为C是不支持C++的么,后来想通了,正是由于BREW平台是基于COM机制的封装,对于最终客户来说,他所能接触到的就是以下这些函数:IICAT_AddRef、IICAT_Release、IICAT_QueryInterface、IICAT_IgnoreMaster、IICat_New。他们只需要调用这些函数,来完成自己的功能,而无须理解其中的实现,无须控制其中的成员变量,而这样些实现,都是编译成二进制代码(比如dll文件),不需要开发人员再次编译。这样就充分体现了COM机制的优越性。

2、在研读的过程中,发现了一个这样的语句
typedef struct _IICat IICat;

觉得很奇怪,作者并没有给出IICat的具体实现,但是IICat却实实在在的存在于代码中间。经过仔细察看,发现原来那些函数把IICat作为传入参数,可是在函数中,对于这个参数的处理,使强制转换为CICat类型,这样,才能去访问CICat中IICatVtbl的那些函数指针。
这么看来,其实IICat是个假的,什么都不是的结构体,他只是CICat的代言人而已,真正干活的是CICat。(感觉有点《鼠胆龙威》里头的张学友和李连杰,呵呵)。

开始一直不明白,大家都提到对于IICatVtbl在结构体中定义的位置,必须放在所有结构体成员的第一位,不然,就无法定位到IICatVtbl中的函数指针。现在,我猜是,由于从IICat转换到CICat,经过了类型转换,其实两者并不是一个类型,但是由于IICatVtbl位于结构体定义的第一位,那么IICat指针所指向的位置也就是IICatVtbl中指针所指向的位置,这样就能保证顺利的访问IICatVtbl中的函数指针。

非常感谢Qinix 斑竹写出了这么好的文章,让大家能从中学习到不少知识,对于文章的理解,还是不够透彻,如果有说得不对的地方,希望大家指正。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值