Mp_iwindow_setvtbl | developer.brewmp.com Mp_iwindow_setvtbl | developer.brewmp.com

Developer

Mp_iwindow_setvtbl

Forums:

in the media player sample app, there is a call in CPlayerWin_New

MP_IWINDOW_SETVTBL(&vtbl, CPlayerWin_Enable, CPlayerWin_Redraw, CPlayerWin_HandleEvent, CPlayerWin_Delete);

where MP_IWINDOW_SETVTBL is a macro
#define MP_IWINDOW_SETVTBL(pVtbl, pfnEn, pfnRd, pfnHE, pfnDel) \
(pVtbl)->Enable = (pfnEn); \
(pVtbl)->Redraw = (pfnRd); \
(pVtbl)->HandleEvent = (pfnHE); \
(pVtbl)->Delete = (pfnDel)

pVtbl is declared as
VTBL(IWindow) vtbl;

does any one know what this means? where do you look up VTBL? are these standard call back methods for a window? when are they called? who calls them?

This is defined in AEE.h:
#define VTBL(iname) iname##Vtbl
basically just creates a name of a virtual table, so the following:
VTBL(myTable) will be converted to myTableVtbl at compile time

This is defined in AEE.h:
#define VTBL(iname) iname##Vtbl
basically just creates a name of a virtual table, so the following:
VTBL(myTable) will be converted to myTableVtbl at compile time

在BREW平台环境下,以C语言实现的面向对象机制,实际上最主要的工作原理体现在对虚函数表的操作上。在BREW中,最基本的接口是IBase,其他绝大部分接口均“继承”了这个接口(在如SmallTalk、Java等纯面向对象语言中,继承有两种形式,“实现”和“扩展”。对接口的继承称为实现,对类的继承称为扩展)。从IBase在整个系统中的地位以及从单纯的面向对象思想来说,IBase就相当于Java语言中的Object对象,不同之处在于,IBase中没有数据部分,是一个纯粹的接口。下面我们来分析一下C语言是如何实现这个接口的。
在BREW所提供的AEE.h和AEEInterface.h中,分别定义了如下预处理指令:
#define VTBL(iname) iname##Vtbl
#define INHERIT_IBase(iname) \
uint32 (*AddRef) (iname*);\
uint32 (*Release) (iname*)
#define QINTERFACE(iname) struct _##iname {\
struct VTBL(iname) *pvt;\
;\
typedef struct VTBL(iname) VTBL(iname);\
struct VTBL(iname)
接着,在AEE.h中,结构体IBase是这样被定义的:
typedef struct _Ibase IBase;
QINTERFACE(IBase)
{
INHERIT_IBase(IBase);
;
将上面的代码翻译成不包含预处理指令的标准C代码如下:
typedef struct _IBase IBase;
struct _IBase {
struct IBaseVtbl *pvt;
;
typedef struct IBaseVtbl IBaseVtbl;
struct IBaseVtbl {
uint32 (*AddRef)(IBase*);
uint32 (*Release)(IBase*);
;
从上面的代码我们可以看出,在结构体IBase中,其实只有一个元素,它是一个指向结构体IBaseVtbl的指针。这个IBaseVtbl就是IBase的虚函数表。在结构体IBaseVtbl中,声明了两个函数指针,这两个函数指针最终对应了IBase接口提供的两个函数:
IBASE_AddRef(p)和IBASE_Release(p)函数。
这两个函数也是通过预处理指令定义的,其代码如下(AEE.h文件中):
#define GET_PVTBL(p,iname) ((iname*)p)->pvt
#define IBASE_AddRef(p) GET_PVTBL(p,IBase)->AddRef(p)
#define IBASE_Release(p) GET_PVTBL(p,IBase)->Release(p)
将GET_PVTBL(p,iname)译成不含预处理指令的标准C代码后,这两个函数表示为:
#define IBASE_AddRef(p) ((IBase *)p)->pvt->AddRef(p)
#define IBASE_Release(p) ((IBase *)p)->pvt->Release(p)
这样,这两个函数接口就被影射到虚函数表的对应函数指针上。
现在超类已经有了,那么BREW又是怎样实现继承的呢? IApplet接口实现了(实际上是“虚实现”) IBase的所有两个函数,还拥有一个属于自己的函数。下面我们来分析一下IApplet是如何继承IBase的。
在AEE.h中,定义IApplet的预处理指令为:
#define INHERIT_IApplet(iname) \
INHERIT_IBase(iname); \
boolean (*HandleEvent)(iname * po, AEEEvent evt, uint16 wp, uint32 dwp)
typedef struct _IApplet IApplet;
QINTERFACE(IApplet)
{
INHERIT_IApplet(IApplet);
;
#define IAPPLET_AddRef(p) GET_PVTBL(p,IApplet)->AddRef(p)
#define IAPPLET_Release(p) GET_PVTBL(p,IApplet)->Release(p)
翻译为不含有预处理指令的C代码为(与IBase代码不同之处已用异色表示):
typedef struct _IApplet IApplet;
struct _IApplet {
struct IAppletVtbl *pvt;
;
typedef struct IAppletVtbl IAppletVtbl;
struct IAppletVtbl {
uint32 (*AddRef)(IApplet*);
uint32 (*Release)(IApplet*);
boolean (*HandleEvent)(iname * po, AEEEvent evt, uint16 wp, uint32 dwp)
;
这说明,IApplet对IBase的继承,只是在IBaseVtbl虚函数表的基础上增加了一个函数,构成了IApplet自己的虚函数表IAppletVtbl。以此类推我们就可以知道,其他高通BREW平台所提供的接口,也是这样以扩充虚函数表的方法达到了继承IBase接口的目的,区别不过是扩充程度的大小而已。以上就是BREW中接口继承接口的原理。
那么如果一个类想要继承接口,BREW又是如何做的呢?
我们以AEEAppGen.c文件中的AEEApplet结构体为例。AEEApplet结构体既实现了IApplet接口的方法,又有自己的数据部分,宽泛地说,它已经符合面向对象的思想,是一个类了。
下面是翻译为无预处理指令的AEEApplet结构体的定义代码:
struct _AEEApplet {
IApplet vtIApplet; //IApple结构体参见上文
uint32 clsID;
IShell * m_pIShell;
IModule * m_pIModule;
IDisplay * m_pIDisplay;
AEEHANDLER pAppHandleEvent //void * 型函数指针
PFNFREEAPPDATA pFreeAppData //void * 型函数指针

这里将AEEApplet结构体中的第一个元素声明为IApplet结构体,这样做有一个好处(也必须这样做),那就是在内存中,AEEApplet对象的地址,其实也是这个对象中的第一个元素——IApplet对象——的地址。这样,指向AEEApplet对象的指针,其实也可以被看作指向IApplet对象的指针。这样就完成了面向对象最基本的继承:一个类的对象,既是它自身类的实例,也是它的父类的实例。
以上就是BREW中类对接口的继承。类对类的继承与上述思想也是一致的。通常,你必须将父类的对象声明为子类的第一个元素,以保证它们将使用同一个内存地址,之后便可以自由定义任何你需要的数据域了。比如:
typedef struct _MyApplet {
AEEApplet a;
int num;
MyApplet, * MyAppletPtr;
不过由于BREW的面向对象是使用C语言实现的,是轻量级的,也就是说,在“面向对象化”的过程中,大部分的工作还是需要你自己(或者借助BREW提供的Helper Functions)来完成。比如在程序运行过程中,如何实例化一个类对象?你要如何为你的类分配内存,初始化虚函数表,注册事件处理器建立事件分发表,然后返回新创建的类实例?如果这些问题在每实例化一个类对象之前都要考虑,那么程序员的寿命应该过不了45岁(哈哈^_^)。幸运的是,高通为我们提供了一个辅助函数,AEEApplet_New(),为我们完成了几乎所有的后台工作。
虽然如此,其实还是有必要参观一下AEEApplet_New()内部是怎么完成工作的,这将有助于我们对BREW平台更加深入的理解。
boolean AEEApplet_New(int16 nIn,
AEECLSID clsID,
IShell * pIShell,
IModule * pIModule,
IApplet **ppobj,
AEEHANDLER pAppHandleEvent,//事件处理器函数指针
PFNFREEAPPDATA pFreeAppData)//析构函数指针
{
AEEApplet * pme = NULL;
IAppletVtbl * appFuncs;
int nSize;
if(nIn < 0)
return(FALSE);
nSize = (int)nIn;
if(!ppobj)
return(FALSE);
*ppobj = NULL;
//Validate parameters
if (!pIShell || !pIModule)
return FALSE;
if(nSize < sizeof(AEEApplet))
nSize += sizeof(AEEApplet);
// Create an Applet object
if (NULL == (pme = (AEEApplet*)MALLOC(nSize + sizeof(IAppletVtbl))))
return FALSE;
appFuncs = (IAppletVtbl *)((byte *)pme + nSize);
//Initialize the individual entries in the VTBL
appFuncs->AddRef = AEEApplet_AddRef;
appFuncs->Release = AEEApplet_Release;
appFuncs->HandleEvent = AEEApplet_HandleEvent;
INIT_VTBL(pme, IApplet, *appFuncs); // Initialize the VTBL
//Initialize the data members
pme->m_nRefs = 1;
pme->m_pIShell = pIShell;
pme->m_pIModule = pIModule;
pme->m_pIDisplay = NULL;
pme->clsID = clsID;
//Store the function pointers to APP's HandleEvent and FreeAppData functions
pme->pAppHandleEvent = pAppHandleEvent;
pme->pFreeAppData = pFreeAppData;
ISHELL_CreateInstance(pIShell, AEECLSID_DISPLAY,
(void **)&pme->m_pIDisplay);
if (!pme->m_pIDisplay) {
//Cleanup
FREE_VTBL(pme, IApplet);
FREE(pme);
return FALSE;
}
ISHELL_AddRef(pIShell);
IMODULE_AddRef(pIModule);
*ppobj = (IApplet*)pme;
return TRUE;

首先,说明一下为什么要传入一个指针的指针(IApplet * * ppobj)作为参数。这是因为在C语言中,参数是以值传递的形式产生作用的,函数不能直接改变实参的值而只能改变形参的值。因此要想使函数改变外部变量的值就必须在函数中操作该外部变量的地址;相应的,要想改变外部指针变量的值就必须向函数传入、并在函数中操作该指针变量的地址——这也就是所谓指针的指针。
其次,我们还可以看到面向对象的思想:**ppobj是IApplet 类型,但它也可能是任何继承了IApplet接口的类对象。这体现了面向对象的继承和多态。
进入函数体,函数执行步骤如下:
1. 安全检查,看看传进来的参数是否合法,并做简略的初始化工作。
2. 判断nSize的大小,nSize是要创建的类所占的byte数。其中nSize不能小于AEEApplet的尺寸(当然如此,因为你的应用类至少要继承AEEApplet嘛),否则就要执行代码:
nSize += sizeof(AEEApplet);
3. 为类实例分配内存,这是关键的步骤之一:
pme = (AEEApplet*)MALLOC(nSize + sizeof(IAppletVtbl))
这里得到的pme虽然是AEEApplet类对象的指针,但这段内存的大小却是你要创建的那个自定义类的大小,再加上IAppletVtbl虚函数表的大小。
4. 将appFuncs指向新分配内存区后半部分的虚函数表
appFuncs = (IAppletVtbl *)((byte *)pme + nSize);
5. 初始化虚函数表的指针:
appFuncs->AddRef = AEEApplet_AddRef;
appFuncs->Release = AEEApplet_Release;
appFuncs->HandleEvent = AEEApplet_HandleEvent;
6. 也是关键步骤之一,初始化虚函数表:
INIT_VTBL(pme, IApplet, *appFuncs);
这是一个很复杂的嵌套预处理指令,其中涉及到AEEGlobalFunctionTable(全局函数表)的操作,更深入的研究对于我们应用层上的程序员来说似乎没有太大的意义。这个步骤完成的任务是,将新创建的类的虚函数表添加到全局函数表中,并让自己持有的虚函数表指针指向它。如果全局函数表中已经存在该类的虚函数表,那么不再添加虚函数表,仅将自己的虚函数表指针指向已经存在的虚函数表。这样一来,某一个类的所有实例的虚函数表指针都指向同一个虚函数表,这便保证了它们所拥有的方法其实全部指向同一段代码。
7. 初始化AEEApplet自己的成员变量。其中包括调用ISHELL_CreateInstance()函数产生一个AEECLSID_DISPLAY类对象。还包括初始化事件处理器函数指针和析构函数的函数指针。需要说明的是,此时虚函数表中的函数指针已经初始化完毕,其中HandleEvent指针并没有初始化为你所传递的参数pAppHandleEvent,而是指向AEEApplet自己的事件处理器函数AEEApplet_HandleEvent。不过不用担心,AEEApplet_HandleEvent函数在实际运行过程中调用了你提供的事件处理器,它只不过起到一个穿针引线的作用,因为在该函数中,只有一行return代码:
return ((AEEApplet *)po)->pAppHandleEvent(po, evt,wParam,dwParam);
它动态地返回了你传入的函数指针参数。至于系统又是如何将这个事件处理器注册到系统事件分发表中的,就不关我们的事了。
8. 到这里,该创建的创建了,该申请的申请了,该初始化的也初始化了。下面就该返回结果了:
*ppobj = (IApplet*)pme;
这一行代码之后,意味着整个AEEApplet_New()的工作终于结束了。
关于回调函数中体现的面向对象思想
(对该部分的理解尚未成熟)
从上面的分析我们还可以看出,所有的事件处理器函数都是形如以下代码的函数:
boolean CallBackFunction((IApplet *)po, uint32 evt, uint16 wParam, uint32 dwParam)
这些函数又叫做回调函数,它们被统一放在系统的事件分发表中,由系统负责向该表分发事件。只要你的应用程序需要接收事件,那么只要向系统事件分发表注册你的事件处理器即可。由于这些回调函数规格已定,所以系统不必详细了解你的事件处理器具体完成什么工作,甚至不需要知道你的处理器函数叫什么名字,它只要知道一个内存地址,便会在系统运行期动态地调用回调函数向你的应用程序发送事件。
我个人认为这个机制很类似面向对象编成模式中的“观察者模式”。[GOF95]对观察者模式的定义是:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
Java语言很好地支持了该模式,它提供了一个Observable类,以及一个 Observer接口。
Observer接口中只有一个方法,即update()方法。当被观察者对象状态发生变化时,它就调用这一方法来通知继承Observer接口的观察者对象。
Observable类对象中包括一个保存观察者对象引用的列表obs,和一个notifyObservers()方法。想要监听Observable类对象的观察者们都要向列表obs中注册自己(就是把自己的引用添加到obs列表中)。一旦它们监听的主题发生了变化,Observable类对象就会调用notifyObservers()方法遍历obs列表,然后依次调用每一个观察者的update()方法以通知这些观察者们自动更新。
在面向对象的BREW平台上,它的事件驱动机制十分符合以上编程模式的思想。整个BREW系统就好像一个大的Observable类对象的实例,而平台上(包括Shell在内?)的每一个应用程序,都是一个观察者,他们都继承了相同的接口——IApplet,而EventHandler()恰好相当于update()。系统事件分发表,如同Observable类对象中的obs列表,一旦有新的事件发生,系统就遍历事件分发表,依次调用每一个回调函数事件处理器,以便通知应用程序进行响应。
这些相似的对比告诉我们,我们所实现的BREW平台应用程序,实际上仅仅是一个“类”的实例而已。BREW系统是一个更大的应用程序,在运行中动态加载和释放我们提供的应用程序类。这也与用Java等纯面向对象程序设计语言实现的应用程序如出一辙。
if you can understand chinese.....
gookluck!

在BREW平台环境下,以C语言实现的面向对象机制,实际上最主要的工作原理体现在对虚函数表的操作上。在BREW中,最基本的接口是IBase,其他绝大部分接口均“继承”了这个接口(在如SmallTalk、Java等纯面向对象语言中,继承有两种形式,“实现”和“扩展”。对接口的继承称为实现,对类的继承称为扩展)。从IBase在整个系统中的地位以及从单纯的面向对象思想来说,IBase就相当于Java语言中的Object对象,不同之处在于,IBase中没有数据部分,是一个纯粹的接口。下面我们来分析一下C语言是如何实现这个接口的。
在BREW所提供的AEE.h和AEEInterface.h中,分别定义了如下预处理指令:
#define VTBL(iname) iname##Vtbl
#define INHERIT_IBase(iname) \
uint32 (*AddRef) (iname*);\
uint32 (*Release) (iname*)
#define QINTERFACE(iname) struct _##iname {\
struct VTBL(iname) *pvt;\
;\
typedef struct VTBL(iname) VTBL(iname);\
struct VTBL(iname)
接着,在AEE.h中,结构体IBase是这样被定义的:
typedef struct _Ibase IBase;
QINTERFACE(IBase)
{
INHERIT_IBase(IBase);
;
将上面的代码翻译成不包含预处理指令的标准C代码如下:
typedef struct _IBase IBase;
struct _IBase {
struct IBaseVtbl *pvt;
;
typedef struct IBaseVtbl IBaseVtbl;
struct IBaseVtbl {
uint32 (*AddRef)(IBase*);
uint32 (*Release)(IBase*);
;
从上面的代码我们可以看出,在结构体IBase中,其实只有一个元素,它是一个指向结构体IBaseVtbl的指针。这个IBaseVtbl就是IBase的虚函数表。在结构体IBaseVtbl中,声明了两个函数指针,这两个函数指针最终对应了IBase接口提供的两个函数:
IBASE_AddRef(p)和IBASE_Release(p)函数。
这两个函数也是通过预处理指令定义的,其代码如下(AEE.h文件中):
#define GET_PVTBL(p,iname) ((iname*)p)->pvt
#define IBASE_AddRef(p) GET_PVTBL(p,IBase)->AddRef(p)
#define IBASE_Release(p) GET_PVTBL(p,IBase)->Release(p)
将GET_PVTBL(p,iname)译成不含预处理指令的标准C代码后,这两个函数表示为:
#define IBASE_AddRef(p) ((IBase *)p)->pvt->AddRef(p)
#define IBASE_Release(p) ((IBase *)p)->pvt->Release(p)
这样,这两个函数接口就被影射到虚函数表的对应函数指针上。
现在超类已经有了,那么BREW又是怎样实现继承的呢? IApplet接口实现了(实际上是“虚实现”) IBase的所有两个函数,还拥有一个属于自己的函数。下面我们来分析一下IApplet是如何继承IBase的。
在AEE.h中,定义IApplet的预处理指令为:
#define INHERIT_IApplet(iname) \
INHERIT_IBase(iname); \
boolean (*HandleEvent)(iname * po, AEEEvent evt, uint16 wp, uint32 dwp)
typedef struct _IApplet IApplet;
QINTERFACE(IApplet)
{
INHERIT_IApplet(IApplet);
;
#define IAPPLET_AddRef(p) GET_PVTBL(p,IApplet)->AddRef(p)
#define IAPPLET_Release(p) GET_PVTBL(p,IApplet)->Release(p)
翻译为不含有预处理指令的C代码为(与IBase代码不同之处已用异色表示):
typedef struct _IApplet IApplet;
struct _IApplet {
struct IAppletVtbl *pvt;
;
typedef struct IAppletVtbl IAppletVtbl;
struct IAppletVtbl {
uint32 (*AddRef)(IApplet*);
uint32 (*Release)(IApplet*);
boolean (*HandleEvent)(iname * po, AEEEvent evt, uint16 wp, uint32 dwp)
;
这说明,IApplet对IBase的继承,只是在IBaseVtbl虚函数表的基础上增加了一个函数,构成了IApplet自己的虚函数表IAppletVtbl。以此类推我们就可以知道,其他高通BREW平台所提供的接口,也是这样以扩充虚函数表的方法达到了继承IBase接口的目的,区别不过是扩充程度的大小而已。以上就是BREW中接口继承接口的原理。
那么如果一个类想要继承接口,BREW又是如何做的呢?
我们以AEEAppGen.c文件中的AEEApplet结构体为例。AEEApplet结构体既实现了IApplet接口的方法,又有自己的数据部分,宽泛地说,它已经符合面向对象的思想,是一个类了。
下面是翻译为无预处理指令的AEEApplet结构体的定义代码:
struct _AEEApplet {
IApplet vtIApplet; //IApple结构体参见上文
uint32 clsID;
IShell * m_pIShell;
IModule * m_pIModule;
IDisplay * m_pIDisplay;
AEEHANDLER pAppHandleEvent //void * 型函数指针
PFNFREEAPPDATA pFreeAppData //void * 型函数指针

这里将AEEApplet结构体中的第一个元素声明为IApplet结构体,这样做有一个好处(也必须这样做),那就是在内存中,AEEApplet对象的地址,其实也是这个对象中的第一个元素——IApplet对象——的地址。这样,指向AEEApplet对象的指针,其实也可以被看作指向IApplet对象的指针。这样就完成了面向对象最基本的继承:一个类的对象,既是它自身类的实例,也是它的父类的实例。
以上就是BREW中类对接口的继承。类对类的继承与上述思想也是一致的。通常,你必须将父类的对象声明为子类的第一个元素,以保证它们将使用同一个内存地址,之后便可以自由定义任何你需要的数据域了。比如:
typedef struct _MyApplet {
AEEApplet a;
int num;
MyApplet, * MyAppletPtr;
不过由于BREW的面向对象是使用C语言实现的,是轻量级的,也就是说,在“面向对象化”的过程中,大部分的工作还是需要你自己(或者借助BREW提供的Helper Functions)来完成。比如在程序运行过程中,如何实例化一个类对象?你要如何为你的类分配内存,初始化虚函数表,注册事件处理器建立事件分发表,然后返回新创建的类实例?如果这些问题在每实例化一个类对象之前都要考虑,那么程序员的寿命应该过不了45岁(哈哈^_^)。幸运的是,高通为我们提供了一个辅助函数,AEEApplet_New(),为我们完成了几乎所有的后台工作。
虽然如此,其实还是有必要参观一下AEEApplet_New()内部是怎么完成工作的,这将有助于我们对BREW平台更加深入的理解。
boolean AEEApplet_New(int16 nIn,
AEECLSID clsID,
IShell * pIShell,
IModule * pIModule,
IApplet **ppobj,
AEEHANDLER pAppHandleEvent,//事件处理器函数指针
PFNFREEAPPDATA pFreeAppData)//析构函数指针
{
AEEApplet * pme = NULL;
IAppletVtbl * appFuncs;
int nSize;
if(nIn < 0)
return(FALSE);
nSize = (int)nIn;
if(!ppobj)
return(FALSE);
*ppobj = NULL;
//Validate parameters
if (!pIShell || !pIModule)
return FALSE;
if(nSize < sizeof(AEEApplet))
nSize += sizeof(AEEApplet);
// Create an Applet object
if (NULL == (pme = (AEEApplet*)MALLOC(nSize + sizeof(IAppletVtbl))))
return FALSE;
appFuncs = (IAppletVtbl *)((byte *)pme + nSize);
//Initialize the individual entries in the VTBL
appFuncs->AddRef = AEEApplet_AddRef;
appFuncs->Release = AEEApplet_Release;
appFuncs->HandleEvent = AEEApplet_HandleEvent;
INIT_VTBL(pme, IApplet, *appFuncs); // Initialize the VTBL
//Initialize the data members
pme->m_nRefs = 1;
pme->m_pIShell = pIShell;
pme->m_pIModule = pIModule;
pme->m_pIDisplay = NULL;
pme->clsID = clsID;
//Store the function pointers to APP's HandleEvent and FreeAppData functions
pme->pAppHandleEvent = pAppHandleEvent;
pme->pFreeAppData = pFreeAppData;
ISHELL_CreateInstance(pIShell, AEECLSID_DISPLAY,
(void **)&pme->m_pIDisplay);
if (!pme->m_pIDisplay) {
//Cleanup
FREE_VTBL(pme, IApplet);
FREE(pme);
return FALSE;
}
ISHELL_AddRef(pIShell);
IMODULE_AddRef(pIModule);
*ppobj = (IApplet*)pme;
return TRUE;

首先,说明一下为什么要传入一个指针的指针(IApplet * * ppobj)作为参数。这是因为在C语言中,参数是以值传递的形式产生作用的,函数不能直接改变实参的值而只能改变形参的值。因此要想使函数改变外部变量的值就必须在函数中操作该外部变量的地址;相应的,要想改变外部指针变量的值就必须向函数传入、并在函数中操作该指针变量的地址——这也就是所谓指针的指针。
其次,我们还可以看到面向对象的思想:**ppobj是IApplet 类型,但它也可能是任何继承了IApplet接口的类对象。这体现了面向对象的继承和多态。
进入函数体,函数执行步骤如下:
1. 安全检查,看看传进来的参数是否合法,并做简略的初始化工作。
2. 判断nSize的大小,nSize是要创建的类所占的byte数。其中nSize不能小于AEEApplet的尺寸(当然如此,因为你的应用类至少要继承AEEApplet嘛),否则就要执行代码:
nSize += sizeof(AEEApplet);
3. 为类实例分配内存,这是关键的步骤之一:
pme = (AEEApplet*)MALLOC(nSize + sizeof(IAppletVtbl))
这里得到的pme虽然是AEEApplet类对象的指针,但这段内存的大小却是你要创建的那个自定义类的大小,再加上IAppletVtbl虚函数表的大小。
4. 将appFuncs指向新分配内存区后半部分的虚函数表
appFuncs = (IAppletVtbl *)((byte *)pme + nSize);
5. 初始化虚函数表的指针:
appFuncs->AddRef = AEEApplet_AddRef;
appFuncs->Release = AEEApplet_Release;
appFuncs->HandleEvent = AEEApplet_HandleEvent;
6. 也是关键步骤之一,初始化虚函数表:
INIT_VTBL(pme, IApplet, *appFuncs);
这是一个很复杂的嵌套预处理指令,其中涉及到AEEGlobalFunctionTable(全局函数表)的操作,更深入的研究对于我们应用层上的程序员来说似乎没有太大的意义。这个步骤完成的任务是,将新创建的类的虚函数表添加到全局函数表中,并让自己持有的虚函数表指针指向它。如果全局函数表中已经存在该类的虚函数表,那么不再添加虚函数表,仅将自己的虚函数表指针指向已经存在的虚函数表。这样一来,某一个类的所有实例的虚函数表指针都指向同一个虚函数表,这便保证了它们所拥有的方法其实全部指向同一段代码。
7. 初始化AEEApplet自己的成员变量。其中包括调用ISHELL_CreateInstance()函数产生一个AEECLSID_DISPLAY类对象。还包括初始化事件处理器函数指针和析构函数的函数指针。需要说明的是,此时虚函数表中的函数指针已经初始化完毕,其中HandleEvent指针并没有初始化为你所传递的参数pAppHandleEvent,而是指向AEEApplet自己的事件处理器函数AEEApplet_HandleEvent。不过不用担心,AEEApplet_HandleEvent函数在实际运行过程中调用了你提供的事件处理器,它只不过起到一个穿针引线的作用,因为在该函数中,只有一行return代码:
return ((AEEApplet *)po)->pAppHandleEvent(po, evt,wParam,dwParam);
它动态地返回了你传入的函数指针参数。至于系统又是如何将这个事件处理器注册到系统事件分发表中的,就不关我们的事了。
8. 到这里,该创建的创建了,该申请的申请了,该初始化的也初始化了。下面就该返回结果了:
*ppobj = (IApplet*)pme;
这一行代码之后,意味着整个AEEApplet_New()的工作终于结束了。
关于回调函数中体现的面向对象思想
(对该部分的理解尚未成熟)
从上面的分析我们还可以看出,所有的事件处理器函数都是形如以下代码的函数:
boolean CallBackFunction((IApplet *)po, uint32 evt, uint16 wParam, uint32 dwParam)
这些函数又叫做回调函数,它们被统一放在系统的事件分发表中,由系统负责向该表分发事件。只要你的应用程序需要接收事件,那么只要向系统事件分发表注册你的事件处理器即可。由于这些回调函数规格已定,所以系统不必详细了解你的事件处理器具体完成什么工作,甚至不需要知道你的处理器函数叫什么名字,它只要知道一个内存地址,便会在系统运行期动态地调用回调函数向你的应用程序发送事件。
我个人认为这个机制很类似面向对象编成模式中的“观察者模式”。[GOF95]对观察者模式的定义是:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
Java语言很好地支持了该模式,它提供了一个Observable类,以及一个 Observer接口。
Observer接口中只有一个方法,即update()方法。当被观察者对象状态发生变化时,它就调用这一方法来通知继承Observer接口的观察者对象。
Observable类对象中包括一个保存观察者对象引用的列表obs,和一个notifyObservers()方法。想要监听Observable类对象的观察者们都要向列表obs中注册自己(就是把自己的引用添加到obs列表中)。一旦它们监听的主题发生了变化,Observable类对象就会调用notifyObservers()方法遍历obs列表,然后依次调用每一个观察者的update()方法以通知这些观察者们自动更新。
在面向对象的BREW平台上,它的事件驱动机制十分符合以上编程模式的思想。整个BREW系统就好像一个大的Observable类对象的实例,而平台上(包括Shell在内?)的每一个应用程序,都是一个观察者,他们都继承了相同的接口——IApplet,而EventHandler()恰好相当于update()。系统事件分发表,如同Observable类对象中的obs列表,一旦有新的事件发生,系统就遍历事件分发表,依次调用每一个回调函数事件处理器,以便通知应用程序进行响应。
这些相似的对比告诉我们,我们所实现的BREW平台应用程序,实际上仅仅是一个“类”的实例而已。BREW系统是一个更大的应用程序,在运行中动态加载和释放我们提供的应用程序类。这也与用Java等纯面向对象程序设计语言实现的应用程序如出一辙。
if you can understand chinese.....
gookluck!

C++:BREW上的面向对象语言
用C语言实现面向对象,不是不可以,因为C语言很强大;但这实在有点儿像公鸡下蛋——不该他干的事儿,他要干。既然如此,那么当一个正宗的母鸡来到高通平台上时,她又能不能下出蛋来,蛋的质量又如何呢?下面我们看看名门正派的面向对象语言C++在BREW平台上是如何一展身手的。在高通BREW平台上,一个用C++编写的Helloworld程序的代码通常是这样的:
//定义新的类MyApp
class MyApp : public AEEApplet{
public:
//注意这两个静态函数
static boolean HandleEvent(MyApp * pMyApp, AEEEvent eCode, uint16 wParam,
uint32 dwParam);
static void freeAppData(MyApp * pMyApp);
protected:
void OnAppfreeData();
boolean OnEvent(AEEEvent eCode,  uint16 wParam,  uint32 dwParam);
;
//模块入口函数
extern "C" int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * pIModule,
void ** ppApplet) {
AEEApplet* pMe = 0;
*ppApplet = NULL;
if(ClsId == AEECLSID_CPPHELLO_BID ) {
if(!AEEApplet_New(sizeof(MyApp), ClsId, pIShell,
pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,//静态函数
(PFNFREEAPPDATA)MyApp::freeAppData)//静态函数
)return EFAILED;
pMe = (AEEApplet*)(*ppApplet);
if (!pMe)  return(EFAILED);
return(SUCCESS);
} else  return(EFAILED);

//静态函数代码
void MyApp::freeAppData(MyApp *pApp){
pApp->OnAppfreeData();

//静态函数代码
boolean MyApp::HandleEvent(MyApp *pApp, AEEEvent eCode, uint16 wParam, uint32 dwParam){
return pApp->OnEvent(eCode, wParam, dwParam);

//MyApp内部函数代码
boolean MyApp::OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam) {
AECHAR szBuf[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd','\0'};
switch (eCode) {
case EVT_APP_START:
IDISPLAY_ClearScreen(m_pIDisplay);
IDISPLAY_DrawText(m_pIDisplay, AEE_FONT_BOLD,szBuf,
-1, 0, 0, 0, IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
IDISPLAY_Update(m_pIDisplay);
return(TRUE);
break;

return(FALSE);

对于一个习惯了面向对象编程的C++或Java程序员来说,这样不伦不类的代码是让人难以理解的。本来,面向对象的“封装”思想让我们将责任“分权”,对象必须且只需向自己负责。也就是说,正常情况下对象都是应该由自己的构造函数创建的。在单纯的面向对象平台上,上述入口函数可能是这样:(Java语言)
boolean AEEClsCreateInstance(Class newClass, IShell iShell, IModule iModule) {
boolean result = false;
if(newClass instanceof MyApp) {
MyApp myApp = new MyApp();
myApp.setIShell(iShell);
myApp.setIModule(iModule);
AEEToolKit tool = new AEEToolKit();
tool.registerApplet(myApp);
result = true;
}
return result;

其中MyApp类可能还需要实现一个Listener接口,该接口有这样几个方法:
boolean eventHandler( AEEEvent eCode, ……);
void disconstructer();
……
但这里显然不是正常情况,因为我们还是需要调用极其面向过程的AEEApplet_New()函数来建立我们的类。Java语言不支持指针操作,内存分配和收回都是由虚拟机完成,在这样底层的环境下未免碍手碍脚。不过C++就不同,她兼容了C语言的全部特性,将C语言变成了她的一个子集,成为了功能最强大的面向对象语言。既然C++这么猛而我又对这段代码这么有成见,那么我们有没有可能在面向对象的C++的程序中,做如下几件事情呢:
1. 由它们的构造函数自己创建自己,而不是使用AEEApplet_New来创建类实例。
2. 将静态的HandleEvent和freeAppData函数改为(本来就应该是)与具体实例相关的。
现在将代码修改如下:
class MyApp : public AEEApplet{
public:
boolean HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam);
void freeAppData(MyApp * pMyApp);
static AEECLSID classId;
MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
if(ClsId != classId) return;
AEEApplet_New(sizeof(MyApp),
ClsId, pIShell,
pIModule,
(IApplet**)ppApplet,
(AEEHANDLER) HandleEvent,
(PFNFREEAPPDATA) freeAppData)

;
boolean MyApp::HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam){
……

void MyApp::freeAppData(MyApp * pMyApp){
……

这样一段C++代码看起来很完美了,它在不改动工具文件AEEAppGen.h所提供的接口的情况下,把本来十分过程化的类对象创建过程封装在类本身的构造函数中。这样,我们只需要在入口函数AEEClsCreateInstance中调用MyApp构造函数一次,就万事大吉啦!但真的是这样吗?现实,总是残酷的。这段代码在编译时由于一个小小的问题,导致了程序无法编译通过。这个问题出在哪儿呢?出在我们传入的HandleEvent指针类型与人家AEEApplet_New想要的参数类型——AEEHANDLER——不符!
不对啊,我们来看看AEEHANDLER 在AEE.h头文件中是怎么定义的:
typedef boolean (*AEEHANDLER)(void * pData, AEEEvent evt, uint16 wParam, uint32 lParam);
而我们的boolean HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam);完全符合定义标准,而且它与我们最早定义的那个static函数也丝毫不差。
原因就出在static上。由于AEE环境都是C实现的,在.c文件中声明的AEEHANDLER,默认即为静态函数指针变量。而我们在类MyApp中声明的HandleEvent等函数,虽然在返回值类型、参数数量和参数类型上都完全相同,但是它们的作用域不同,这也是编译器所不允许的(这是肯定的,否则每个类内函数都被当成static函数一上来就被加载,那么面向对象的动态加载机制就显得意义不大了)。因此,上面提到的那两件事情,第一件可以做,但第二件不可以。(参见附录)
好吧,如果一定要static就直接实现static函数好啦,为什么还要搞两个protected函数来多此一举?原因是这样的:如果一个函数是静态函数,那么意味着它是实例无关的,你直接把MyApp::HandleEvent中的内容复制粘贴过来肯定无法编译通过,因为那里面有许多关于MyApp内成员变量的操作(如操作m_pIDisplay等)。而如果你把这个函数修改回原来的C语言函数,那倒可以编译通过,但岂不是又退回到非面向对象的境地了吗。
总之,在BREW环境下,目前是无法使用C++语言进行完美的面向对象编程的。究其根本原因,是在于高通所提供的C环境的限制。BREW在飞速发展,它的底层环境也一定会随之进步,但是要看到,我们想改动一段小小的Helloworld代码尚需要加以考虑,何况完整的AEE底层环境已经十分庞大,内部又包含了十分复杂的设计框架和结构,一旦决定修改必然旷日持久工程浩繁。再加上这又是一个商业平台,涉及到多方利益,实在是牵一发而动全身。在没有得到一个良好的既完整向前兼容又完美面向对象的平台架构之前,AEE恐怕还是C的天下。但使用C++语言来提高我们的编程效率,应该还是很好的选择。
[附录]修改之后“比较”面向对象的C++实现的Helloworld,已经编译运行通过。
class MyApp : public AEEApplet{
public:
//静态函数
static boolean HandleEvent(MyApp * pMyApp, AEEEvent eCode,uint16 wParam,
uint32 dwParam);
static void freeAppData(MyApp * pMyApp);
static AEECLSID classId;
//构造函数,让MyApp类对象在构造函数中自己创建自己。
MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
AEEApplet_New(sizeof(MyApp),ClsId,pIShell, pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,
(PFNFREEAPPDATA)MyApp::freeAppData);

protected:
void OnAppfreeData(){};
boolean OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam);
;
AEECLSID MyApp::classId = 0x87654321;
extern "C"
int AEEClsCreateInstance(AEECLSID ClsId, IShell * pIShell, IModule * pIModule,
void ** ppApplet) {
AEEApplet* pMe = 0;
*ppApplet = NULL;
if(ClsId == MyApp::classId ) {
MyApp myApp(ClsId, pIShell, pIModule, ppApplet);
pMe = (AEEApplet*)(*ppApplet);
if (!pMe) return(EFAILED);
return(SUCCESS);
else return(EFAILED);

void MyApp::freeAppData(MyApp *pApp){
pApp->OnAppfreeData();}
boolean MyApp::HandleEvent(MyApp *pApp, AEEEvent eCode, uint16 wParam,
uint32 dwParam) {
return pApp->OnEvent(eCode, wParam, dwParam);}
boolean MyApp::OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam) {
AECHAR szBuf[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd','\0'};
switch (eCode) {
case EVT_APP_START:
IDISPLAY_ClearScreen(m_pIDisplay);
IDISPLAY_DrawText(m_pIDisplay, AEE_FONT_BOLD, szBuf, -1, 0, 0, 0,
IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
IDISPLAY_Update(m_pIDisplay);
return(TRUE);
break;

return(FALSE);

好,现在我们再来探讨一下上面的这段貌似正确的代码。没错,我说的是“貌似正确”,也就是说,这段代码虽然可以编译通过,且较好地运行,但它依然是有问题的。问题在哪儿呢?就在于构造函数上。我们知道,在面向对象语言中,任何一个类的构造函数都至少完成两件事情:1,分配内存,创建一个类对象;2,初始化该类对象。回想一下我们在构造函数里面手动分配内存了吗?并没有,我们在构造函数中所做的事情,从一开始就仅仅是初始化类对象中的变量而已。这也就说明上面那段代码中的分配内存的工作由构造函数完成了。这就是问题的关键。因为我们在构造函数中,用AEEApplet_New函数又分配了一遍内存,并且指针的指针(**ppApplet)所指向的内存是AEEApplet_New分配的那一块,而不是我们手上的那个MyApp类对象myApp。这就麻烦了,因为我们如今只能初始化(*ppApplet)对象的AEEApplet部分,后面属于MyApp数据类型的部分却如何也无法操作了,因为我们“面向对象化”之后,手头上并不掌握(*ppApplet)指针了。而我们手中的myApp对象的前半部分原本应该初始化为AEEApplet对象,现在却空空如也。
到此,我们终于无奈地掌握了这样一个事实,那就是我们再也无法逃避,只能直接面对那个极度过程化的函数AEEApplet_New了。但我们依然可以想办法仍旧将它封装在MyApp类中,并隐藏MyApp的构造函数,以便消除构造函数被无用的危险。我们可以仿照面向对象编程模式中的“单例模式”,将构造函数声明为private,再声明一个static函数,封装创建对象和初始化对象这两个步骤。示例代码如下:
static boolean MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
boolean result = TRUE;
AEEApplet_New(sizeof(MyApp),ClsId,pIShell, pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,
(PFNFREEAPPDATA)MyApp::freeAppData);
//--------------------------初始化你自己的变量------------------------
……
return result;

i didn't quite understand ,so hope u can understand chinese...

C++:BREW上的面向对象语言
用C语言实现面向对象,不是不可以,因为C语言很强大;但这实在有点儿像公鸡下蛋——不该他干的事儿,他要干。既然如此,那么当一个正宗的母鸡来到高通平台上时,她又能不能下出蛋来,蛋的质量又如何呢?下面我们看看名门正派的面向对象语言C++在BREW平台上是如何一展身手的。在高通BREW平台上,一个用C++编写的Helloworld程序的代码通常是这样的:
//定义新的类MyApp
class MyApp : public AEEApplet{
public:
//注意这两个静态函数
static boolean HandleEvent(MyApp * pMyApp, AEEEvent eCode, uint16 wParam,
uint32 dwParam);
static void freeAppData(MyApp * pMyApp);
protected:
void OnAppfreeData();
boolean OnEvent(AEEEvent eCode,  uint16 wParam,  uint32 dwParam);
;
//模块入口函数
extern "C" int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * pIModule,
void ** ppApplet) {
AEEApplet* pMe = 0;
*ppApplet = NULL;
if(ClsId == AEECLSID_CPPHELLO_BID ) {
if(!AEEApplet_New(sizeof(MyApp), ClsId, pIShell,
pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,//静态函数
(PFNFREEAPPDATA)MyApp::freeAppData)//静态函数
)return EFAILED;
pMe = (AEEApplet*)(*ppApplet);
if (!pMe)  return(EFAILED);
return(SUCCESS);
} else  return(EFAILED);

//静态函数代码
void MyApp::freeAppData(MyApp *pApp){
pApp->OnAppfreeData();

//静态函数代码
boolean MyApp::HandleEvent(MyApp *pApp, AEEEvent eCode, uint16 wParam, uint32 dwParam){
return pApp->OnEvent(eCode, wParam, dwParam);

//MyApp内部函数代码
boolean MyApp::OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam) {
AECHAR szBuf[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd','\0'};
switch (eCode) {
case EVT_APP_START:
IDISPLAY_ClearScreen(m_pIDisplay);
IDISPLAY_DrawText(m_pIDisplay, AEE_FONT_BOLD,szBuf,
-1, 0, 0, 0, IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
IDISPLAY_Update(m_pIDisplay);
return(TRUE);
break;

return(FALSE);

对于一个习惯了面向对象编程的C++或Java程序员来说,这样不伦不类的代码是让人难以理解的。本来,面向对象的“封装”思想让我们将责任“分权”,对象必须且只需向自己负责。也就是说,正常情况下对象都是应该由自己的构造函数创建的。在单纯的面向对象平台上,上述入口函数可能是这样:(Java语言)
boolean AEEClsCreateInstance(Class newClass, IShell iShell, IModule iModule) {
boolean result = false;
if(newClass instanceof MyApp) {
MyApp myApp = new MyApp();
myApp.setIShell(iShell);
myApp.setIModule(iModule);
AEEToolKit tool = new AEEToolKit();
tool.registerApplet(myApp);
result = true;
}
return result;

其中MyApp类可能还需要实现一个Listener接口,该接口有这样几个方法:
boolean eventHandler( AEEEvent eCode, ……);
void disconstructer();
……
但这里显然不是正常情况,因为我们还是需要调用极其面向过程的AEEApplet_New()函数来建立我们的类。Java语言不支持指针操作,内存分配和收回都是由虚拟机完成,在这样底层的环境下未免碍手碍脚。不过C++就不同,她兼容了C语言的全部特性,将C语言变成了她的一个子集,成为了功能最强大的面向对象语言。既然C++这么猛而我又对这段代码这么有成见,那么我们有没有可能在面向对象的C++的程序中,做如下几件事情呢:
1. 由它们的构造函数自己创建自己,而不是使用AEEApplet_New来创建类实例。
2. 将静态的HandleEvent和freeAppData函数改为(本来就应该是)与具体实例相关的。
现在将代码修改如下:
class MyApp : public AEEApplet{
public:
boolean HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam);
void freeAppData(MyApp * pMyApp);
static AEECLSID classId;
MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
if(ClsId != classId) return;
AEEApplet_New(sizeof(MyApp),
ClsId, pIShell,
pIModule,
(IApplet**)ppApplet,
(AEEHANDLER) HandleEvent,
(PFNFREEAPPDATA) freeAppData)

;
boolean MyApp::HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam){
……

void MyApp::freeAppData(MyApp * pMyApp){
……

这样一段C++代码看起来很完美了,它在不改动工具文件AEEAppGen.h所提供的接口的情况下,把本来十分过程化的类对象创建过程封装在类本身的构造函数中。这样,我们只需要在入口函数AEEClsCreateInstance中调用MyApp构造函数一次,就万事大吉啦!但真的是这样吗?现实,总是残酷的。这段代码在编译时由于一个小小的问题,导致了程序无法编译通过。这个问题出在哪儿呢?出在我们传入的HandleEvent指针类型与人家AEEApplet_New想要的参数类型——AEEHANDLER——不符!
不对啊,我们来看看AEEHANDLER 在AEE.h头文件中是怎么定义的:
typedef boolean (*AEEHANDLER)(void * pData, AEEEvent evt, uint16 wParam, uint32 lParam);
而我们的boolean HandleEvent(MyApp* pMyApp, AEEEvent eCode,uint16 wParam,uint32 dwParam);完全符合定义标准,而且它与我们最早定义的那个static函数也丝毫不差。
原因就出在static上。由于AEE环境都是C实现的,在.c文件中声明的AEEHANDLER,默认即为静态函数指针变量。而我们在类MyApp中声明的HandleEvent等函数,虽然在返回值类型、参数数量和参数类型上都完全相同,但是它们的作用域不同,这也是编译器所不允许的(这是肯定的,否则每个类内函数都被当成static函数一上来就被加载,那么面向对象的动态加载机制就显得意义不大了)。因此,上面提到的那两件事情,第一件可以做,但第二件不可以。(参见附录)
好吧,如果一定要static就直接实现static函数好啦,为什么还要搞两个protected函数来多此一举?原因是这样的:如果一个函数是静态函数,那么意味着它是实例无关的,你直接把MyApp::HandleEvent中的内容复制粘贴过来肯定无法编译通过,因为那里面有许多关于MyApp内成员变量的操作(如操作m_pIDisplay等)。而如果你把这个函数修改回原来的C语言函数,那倒可以编译通过,但岂不是又退回到非面向对象的境地了吗。
总之,在BREW环境下,目前是无法使用C++语言进行完美的面向对象编程的。究其根本原因,是在于高通所提供的C环境的限制。BREW在飞速发展,它的底层环境也一定会随之进步,但是要看到,我们想改动一段小小的Helloworld代码尚需要加以考虑,何况完整的AEE底层环境已经十分庞大,内部又包含了十分复杂的设计框架和结构,一旦决定修改必然旷日持久工程浩繁。再加上这又是一个商业平台,涉及到多方利益,实在是牵一发而动全身。在没有得到一个良好的既完整向前兼容又完美面向对象的平台架构之前,AEE恐怕还是C的天下。但使用C++语言来提高我们的编程效率,应该还是很好的选择。
[附录]修改之后“比较”面向对象的C++实现的Helloworld,已经编译运行通过。
class MyApp : public AEEApplet{
public:
//静态函数
static boolean HandleEvent(MyApp * pMyApp, AEEEvent eCode,uint16 wParam,
uint32 dwParam);
static void freeAppData(MyApp * pMyApp);
static AEECLSID classId;
//构造函数,让MyApp类对象在构造函数中自己创建自己。
MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
AEEApplet_New(sizeof(MyApp),ClsId,pIShell, pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,
(PFNFREEAPPDATA)MyApp::freeAppData);

protected:
void OnAppfreeData(){};
boolean OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam);
;
AEECLSID MyApp::classId = 0x87654321;
extern "C"
int AEEClsCreateInstance(AEECLSID ClsId, IShell * pIShell, IModule * pIModule,
void ** ppApplet) {
AEEApplet* pMe = 0;
*ppApplet = NULL;
if(ClsId == MyApp::classId ) {
MyApp myApp(ClsId, pIShell, pIModule, ppApplet);
pMe = (AEEApplet*)(*ppApplet);
if (!pMe) return(EFAILED);
return(SUCCESS);
else return(EFAILED);

void MyApp::freeAppData(MyApp *pApp){
pApp->OnAppfreeData();}
boolean MyApp::HandleEvent(MyApp *pApp, AEEEvent eCode, uint16 wParam,
uint32 dwParam) {
return pApp->OnEvent(eCode, wParam, dwParam);}
boolean MyApp::OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam) {
AECHAR szBuf[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd','\0'};
switch (eCode) {
case EVT_APP_START:
IDISPLAY_ClearScreen(m_pIDisplay);
IDISPLAY_DrawText(m_pIDisplay, AEE_FONT_BOLD, szBuf, -1, 0, 0, 0,
IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);
IDISPLAY_Update(m_pIDisplay);
return(TRUE);
break;

return(FALSE);

好,现在我们再来探讨一下上面的这段貌似正确的代码。没错,我说的是“貌似正确”,也就是说,这段代码虽然可以编译通过,且较好地运行,但它依然是有问题的。问题在哪儿呢?就在于构造函数上。我们知道,在面向对象语言中,任何一个类的构造函数都至少完成两件事情:1,分配内存,创建一个类对象;2,初始化该类对象。回想一下我们在构造函数里面手动分配内存了吗?并没有,我们在构造函数中所做的事情,从一开始就仅仅是初始化类对象中的变量而已。这也就说明上面那段代码中的分配内存的工作由构造函数完成了。这就是问题的关键。因为我们在构造函数中,用AEEApplet_New函数又分配了一遍内存,并且指针的指针(**ppApplet)所指向的内存是AEEApplet_New分配的那一块,而不是我们手上的那个MyApp类对象myApp。这就麻烦了,因为我们如今只能初始化(*ppApplet)对象的AEEApplet部分,后面属于MyApp数据类型的部分却如何也无法操作了,因为我们“面向对象化”之后,手头上并不掌握(*ppApplet)指针了。而我们手中的myApp对象的前半部分原本应该初始化为AEEApplet对象,现在却空空如也。
到此,我们终于无奈地掌握了这样一个事实,那就是我们再也无法逃避,只能直接面对那个极度过程化的函数AEEApplet_New了。但我们依然可以想办法仍旧将它封装在MyApp类中,并隐藏MyApp的构造函数,以便消除构造函数被无用的危险。我们可以仿照面向对象编程模式中的“单例模式”,将构造函数声明为private,再声明一个static函数,封装创建对象和初始化对象这两个步骤。示例代码如下:
static boolean MyApp(AEECLSID ClsId, IShell * pIShell, IModule * pIModule, void ** ppApplet) {
boolean result = TRUE;
AEEApplet_New(sizeof(MyApp),ClsId,pIShell, pIModule,
(IApplet**)ppApplet,
(AEEHANDLER)MyApp::HandleEvent,
(PFNFREEAPPDATA)MyApp::freeAppData);
//--------------------------初始化你自己的变量------------------------
……
return result;

i didn't quite understand ,so hope u can understand chinese...