Brew中的Module的实现方法:【原创作者:shosh,http://www.yuleyx.com/shosh】 我们从AEEModGen.c中提供的用于创建Module对象的对外接口入手,函数如下:01int AEEStaticMod_New(int16 nSize, IShell *pIShell, void *ph, IModule **ppMod,02 PFNMODCreateINST pfnMC,PFNFREEMODDATA pfnMF)03{04 AEEMod *pMe = NULL;05 VTBL(IModule) *modFuncs; ……………省略第6到73行的代码…………………74}第4行申请AEEMod结构体类型的pMe指针,该结构体的定义如下:01//Structure that implements the IModule interface02typedef struct _AEEMod 03{04 DECLARE_VTBL(IModule) // Virtual Table with pointers to IModule functions05 06 uint32 m_nRefs; // Reference count for this module07 IShell * m_pIShell; // Pointer to IShell0809 //Address of CreateInstance function of the module. This is needed for10 // static modules11 PFNMODCreateINST pfnModCrInst; 1213 //Address of the function to free the module data. This is needed for static14 // modules that define their own data.15 PFNFREEMODDATA pfnModFreeData;1617} AEEMod; 在该结构体的第4行,声明了一个Virtual Table(虚拟表:专门用来来存放函数指针的结构体,不过此处是指向该结构体的指针),这个是重点,待会将展开来讲。第6行的m_nRefs是模仿COM中的计数器,第11行和第15行的两个函数指针成员,其中pfnModCrInst是用来存放创建Module本身的函数(地址),而pfnModFreeData是Module自身提供释放自身数据的函数指针。下面我们将结构体中的第四行DECLARE_VTBL(IModule)展开:先来看看DECLARE_VTBL的定义:1// Use as first member of classes that override QINTERFACE()2#define DECLARE_VTBL(iname) iname vt##iname; 所以DECLARE_VTBL(IModule)就是: IModule vtIModule;IModule是一个结构体类型,定义如下:1typedef struct _IModule IModule;所以,我们需要了解_IModule的结构体是什么样子的。我们需要从下面的四行代码入手,不过下面主要是两个宏定义,需要展开才能够明白它们到底是什么东西。1QINTERFACE(IModule)2{3 INHERIT_IModule(IModule);4};下面是所有相关的宏定义,先贴出来,然后再用这些定义将上面的4行慢慢展开:01typedef struct _IModule IModule;02#define INHERIT_IModule(iname) /03 INHERIT_IBase(iname); /04 int (*CreateInstance)(iname * po,IShell * pIShell,AEECLSID ClsId,void ** ppObj); /05 void (*FreeResources)(iname * po, IHeap * ph, IFileMgr * pfm)0607QINTERFACE(IModule)08{09 INHERIT_IModule(IModule);10};11#define IMODULE_AddRef(p) GET_PVTBL(p,IModule)->AddRef(p)12#define IMODULE_Release(p) GET_PVTBL(p,IModule)->Release(p)13#define IMODULE_CreateInstance(p,ps,id,ppo) GET_PVTBL(p,IModule)->CreateInstance(p,ps,id,ppo)14#define IMODULE_FreeResources(p,ph,pfm) GET_PVTBL(p,IModule)->FreeResources(p,ph,pfm)1516#define QINTERFACE(iname) struct _##iname {/17 struct VTBL(iname) *pvt;/18 };/19 typedef struct VTBL(iname) VTBL(iname);/20 struct VTBL(iname)2122#define VTBL(iname) iname##Vtbl2324#define INHERIT_IBase(iname) /25 uint32 (*AddRef) (iname*);/26 uint32 (*Release) (iname*)先将QINTERFACE(IModule)展开,根据上面第16到20行的定义以及第22行VTBL的定义,得到:1struct _IModule {2 struct IModuleVtbl *pvt;3 };4 typedef struct IModuleVtbl IModuleVtbl;5 struct IModuleVtbl再将INHERIT_IModule(IModule);展开,根据上面第2到5行的定义以及第24到26行INHERIT_IBase的定义,得到:1 uint32 (*AddRef) (IModule*);2 uint32 (*Release) (IModule*);3 int (*CreateInstance)(IModule * po,IShell * pIShell,AEECLSID ClsId,void ** ppObj);4 void (*FreeResources)(IModule * po, IHeap * ph, IFileMgr * pfm);将以上结果合并起来,就可以得到如下代码:01struct _IModule02{03 struct IModuleVtbl *pvt;04};05typedef struct IModuleVtbl IModuleVtbl;06struct IModuleVtbl07{08 uint32 (*AddRef) (IModule*);09 uint32 (*Release) (IModule*);10 int (*CreateInstance)(IModule * po,IShell * pIShell,AEECLSID ClsId,void ** ppObj);11 void (*FreeResources)(IModule * po, IHeap * ph, IFileMgr * pfm);12};好,现在来总结一下:前面已经说到DECLARE_VTBL(IModule)就是: IModule vtIModule;,它作为AEEMod结构体的第一个成员。IModule是_IModule的结构体类型,_IModule结构体只有一个成员,就是结构体IModuleVtbl的指针。而IModuleVtbl指向的是虚拟函数表的首地址,所以如果有一个IModule* p的指针,p->pvt就是IModuleVtbl*的类型,再用p->pvt->functionName找到对应的函数。为了方便,虚拟函数表中函数的调用是使用宏来定义的,从结构体IModuleVtbl中我们可以看到Module提供了四种方法,宏定义如下,其中还用到GET_PVTBL宏定义:1#define IMODULE_AddRef(p) GET_PVTBL(p,IModule)->AddRef(p)2#define IMODULE_Release(p) GET_PVTBL(p,IModule)->Release(p)3#define IMODULE_CreateInstance(p,ps,id,ppo) GET_PVTBL(p,IModule)->CreateInstance(p,ps,id,ppo)4#define IMODULE_FreeResources(p,ph,pfm) GET_PVTBL(p,IModule)->FreeResources(p,ph,pfm)5#define GET_PVTBL(p,iname) ((iname*)p)->pvt我们以AddRef函数为例,我们调用的时候会使用"IMODULE_AddRef(p);"语句,将其展开,其实就是:((IModule*)p)->pvt->AddRef(p);这样就实现了虚拟函数表中函数的调用了。 下面再回到用于创建Module对象的对外接口AEEStaticMod_New,具体代码如下:01int AEEStaticMod_New(int16 nSize, IShell *pIShell, void *ph, IModule **ppMod,02 PFNMODCreateINST pfnMC,PFNFREEMODDATA pfnMF)03{04 AEEMod *pMe = NULL;05 VTBL(IModule) *modFuncs; //将其展开就是 IModuleVtbl *modFuncs;0607 if (!ppMod || !pIShell) {08 return EFAILED;09 }1011 if (nSize < 0) {12 return EBADPARM;13 }14 *ppMod = NULL;15 16#ifdef AEE_SIMULATOR17 // IMPORTANT NOTE: g_pvtAEEStdLibEntry global variable is defined for 18 // SDK ONLY! This variable should NOT BE:19 //20 // (1) overwritten 21 // (2) USED DIRECTLY by BREW SDK users. 22 //23 // g_pvtAEEStdLibEntry is used as an entry point to AEEStdLib,24 // DO NOT REMOVE the next five lines.25 if (!ph) {26 return EFAILED;27 } else {28 g_pvtAEEStdLibEntry = (AEEHelperFuncs *)ph;29 }30#endif3132 //Allocate memory for the AEEMod object3334 if (nSize < sizeof(AEEMod)) {35 nSize += sizeof(AEEMod);36 }3738 if (NULL == (pMe = (AEEMod *)MALLOC(nSize + sizeof(IModuleVtbl)))) {39 return ENOMEMORY;40 }41 42 // Allocate the vtbl and initialize it. Note that the modules and apps 43 // must not have any static data. Hence, we need to allocate the vtbl as 44 // well.4546 modFuncs = (IModuleVtbl *)((byte *)pMe + nSize);4748 // Initialize individual entries in the VTBL49 modFuncs->AddRef = AEEMod_AddRef;50 modFuncs->Release = AEEMod_Release;51 modFuncs->CreateInstance = AEEMod_CreateInstance;52 modFuncs->FreeResources = AEEMod_FreeResources;535455 // initialize the vtable56 INIT_VTBL(pMe, IModule, *modFuncs);5758 // initialize the data members5960 // Store address of Module's CreateInstance function61 pMe->pfnModCrInst = pfnMC;6263 // Store Address of Module's FreeData function64 pMe->pfnModFreeData = pfnMF;6566 pMe->m_nRefs = 1;67 pMe->m_pIShell = pIShell;68 ISHELL_AddRef(pIShell);6970 // Set the pointer in the parameter71 *ppMod = (IModule*)pMe;7273 return SUCCESS;74} 代码第38行是申请内存空间,其大小是一个不小于sizeof(AEEMod)的大小和sizeof(IModuleVtbl)的大小的和。因为pMe是AEEMod *类型的,从第56到67行是对其进行赋值,所以,申请到的空间前面部分是存放AEEMod的数据的。再看第46行,将pMe所指向的地址向后偏移nSize个字节后强转为IModuleVtbl *类型,说明刚才分配的内存的最后sizeof(IModuleVtbl)的字节是用来存放虚拟函数表的。再来看第56行的代码,是一个宏,定义如下:1#define INIT_VTBL(p,iname,vt) (GET_PVTBL(p,iname) = (VTBL(iname) *)&vt)23#define GET_PVTBL(p,iname) ((iname*)p)->pvt4#define VTBL(iname) iname##Vtbl所以展开就其实就是:(((IModule*)pMe)->pvt = (IModuleVtbl *)&*modFuncs);所以实际上就是将位于申请过来的空间后面部分的虚拟函数表的首地址让AEEMod的第一个成员的pvt成员保存(实际上就是空间的前4个字节保存虚拟函数表的首地址)。在代码的第71行将pMe强转为IModule*返回,用这返回的IModule*指针,可以调用IModule所提供的方法(可以回看一下刚才提到的函数调用这部分的介绍),最后释放空间的时候,只要释放这个指针就可以了,因为申请的时候是一下子申请一大块内存,然后分成两部分使用的。 使用同样的方法可以分析其他如IShell、IApplet等接口及其使用,它们都应该是类似的。