(备注:此文提到的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 斑竹写出了这么好的文章,让大家能从中学习到不少知识,对于文章的理解,还是不够透彻,如果有说得不对的地方,希望大家指正。