VIA(UI)的类中,有以下几个类是从MailBoxC继承而来,
ApplicationC
ApplicationManagerC
DialogStackC
SoftIconDisplayC
GpteKeyProcessC
WindowC
正是它们构成了VIA UI的主体部分。MailBoxC的一个最主要的功能便是实现了消息映射机制。
消息映射机制的实现主要依赖于几个宏和一个函数。它们分别是,
宏:
BEGIN_MAIL_MAP(ThisClass, BaseClass)
END_MAIL_MAP()
DECLARE_MAIL_MAP()
DEFINE_TERMINATING_MAIL_MAP(ThisClass)
ON_MAIL_NOARGS(MailMsgId, MailFunc)
ON_MAIL_VOIDPTR(MailMsgId, MailFunc)
ON_MAIL_MSGID_VOIDPTR
ON_MAIL_WINDOWMAIL(MailMsgId, MailFunc)
函数:
MailHandlerResultT FindMailHandler(uint32 MailMsgId, void *MailMsgP);
为某个类(必须是MailboxC的继承类)添加一个消息映射,通常我们的做法是:
1. 为这个类定义一个消息,例如:
#define SMSAPP_LAUNCH APPCAT(SMSAPP,1)
2. 为这个消息定义一个映射函数,如:
MailHandlerResultT OnLaunchApp(void *MsgBufferP);
3. 将消息与函数关联起来,根据消息映射函数参数类型的不同,有三种方式,如下:
ON_MAIL_VOIDPTR(SMSAPP_LAUNCH, SMSApplicationC::OnLaunchApp)
ON_MAIL_NOARGS(VTUI_CLOSE_NOTIFY, MenuSettingDialogC::OnCloseNotify)
ON_MAIL_WINDOWMAIL(VTUI_MENU_ITEM_HOVERED, MenuSettingDialogC::OnMenuItemHovered)
4. 此外,一个类如果要实现消息映射机制,必须在声明文件(.h)的尾部使用宏DECLARE_MAIL_MAP(),并在定义文件(.cpp)使用宏BEGIN_MAIL_MAP(ThisClass, BaseClass),END_MAIL_MAP(),而所有这个类的将消息映射函数与消息关联的消息映射宏都必须包含在这个两个宏中间。这些与消息映射相关的宏并没有在语法上有什么新的突破,通过一个实际的例子把这些宏展开,就可以清楚地看到消息映射是如何实现的。
这些宏的定义如下:
#define DECLARE_MAIL_MAP() /
private: /
static const MailMapEntryT MailEntries[]; /
protected: /
static const MailMapT MailMap; /
virtual const MailMapT* GetMailMap() const;
#define BEGIN_MAIL_MAP(ThisClass, BaseClass) /
const MailMapT* ThisClass::GetMailMap() const /
{ return &ThisClass::MailMap; } /
const MailMapT ThisClass::MailMap = /
{ &BaseClass::MailMap, &ThisClass::MailEntries[0] }; /
const MailMapEntryT ThisClass::MailEntries[] = /
{ /
#define END_MAIL_MAP() /
{0, 0, END_HANDLER, (MailHandlerT)0 } /
};
#define ON_MAIL_NOARGS(MailMsgId, MailFunc) /
{ MailMsgId, MailMsgId, NOARGS_HANDLER, /
(MailHandlerT)(MailHandlerResultT(MailboxC::*)())&MailFunc },
#define ON_MAIL_VOIDPTR(MailMsgId, MailFunc) /
{ MailMsgId, MailMsgId, VOIDPTR_HANDLER, /
(MailHandlerT)(MailHandlerResultT(MailboxC::*)(void*))&MailFunc },
#define ON_MAIL_WINDOWMAIL(MailMsgId, MailFunc) /
{ MailMsgId, MailMsgId, WINDOW_HANDLER, /
(MailHandlerT)(MailHandlerResultT(MailboxC::*)(uint32,WindowHandleT,ParamT,ParamT))&MailFunc },
我们取MenuSettingDialogC中的一部分代码展开作为实例来说明。这段代码是,
DECLARE_MAIL_MAP()
BEGIN_MAIL_MAP(MenuSettingDialogC, DialogC)
ON_MAIL_NOARGS(VTUI_CLOSE_NOTIFY, MenuSettingDialogC::OnCloseNotify)
ON_MAIL_WINDOWMAIL(VTUI_MENU_ITEM_HOVERED, MenuSettingDialogC::OnMenuItemHovered)
ON_MAIL_VOIDPTR(VTUI_OPTION_SELECTED, MenuSettingDialogC::OnOptionSelected)
END_MAIL_MAP()
第一句展开:
//DECLARE_MAIL_MAP()
private:
static const MailMapEntryT MailEntries[];
protected:
static const MailMapT MailMap;
virtual const MailMapT* GetMailMap() const;
在这段代码里,宏DECLARE_MAIL_MAP()声明了一个私有成员和两个保护成员,这是这个宏为什么必须放在类声明尾部的原因。如果放在中部和头部,这里的protected:很容易意外地把宏后的其它成员属性改为protected。
这三个成员,都将在类的实现部分被宏BEGIN_MAIL_MAP(ThisClass, BaseClass)初始化或定义。
第二句和最后一句展开:
//BEGIN_MAIL_MAP(MenuSettingDialogC, DialogC)
const MailMapT* MenuSettingDialogC::GetMailMap() const
{ return &MenuSettingDialogC::MailMap; }
const MailMapT MenuSettingDialogC::MailMap =
{ &DialogC::MailMap, &MenuSettingDialogC::MailEntries[0] };
const MailMapEntryT MenuSettingDialogC::MailEntries[] =
{
………
// END_MAIL_MAP()
{0, 0, END_HANDLER, (MailHandlerT)0 }
};
首先注意到,两段代码的最后分别是‘{’和‘}’,这就是两个宏为什么必须成对的原因,如果不成对使用,编译器将会报告括号不匹配的错误。
BEGIN_MAIL_MAP(MenuSettingDialogC, DialogC)宏的作用是初始化和定义前面DECLARE_MAIL_MAP()宏所声明的数据成员MailEntries ,MailMap和成员函数GetMailMap()。我们依次来讨论这三个成员的作用:
1. MailEntries:MailMapEntryT结构体类型的数组,用于存放消息ID及与处理该消息的回调函数指针,类对消息的响应其实质就是对该数组的遍历。MailMapEntryT结构的定义为,
struct MailMapEntryT {
uint32 MailMsgId;
uint32 LastMsgId; // If Handler handles a range of Ids.
MailHandlerTypeT Args;
MailHandlerT MailHandlerP;
};
2. MailMap:MailMapT类型的指针变量,通过它可以获得本类和基类中MailEntries消息映射数组的地址。
struct MailMapT {
const MailMapT* BaseMapP;
const MailMapEntryT* MailEntryP;
};
3. GetMailMap(), const属性的成员函数,正象大多数const函数所做的,得到某个成员变量的值,这里是返回上述成员变量MailMap的值。
END_MAIL_MAP()宏的作用,主要是实现MailEntries数组的正常结束,这里给该数组提供了一个结束标示{0, 0, END_HANDLER, (MailHandlerT)0 },并且保证不会出现‘,’号错误。
剩下的三个宏其实是为消息提供三种不同类型的映射函数,也是为MailEntries数组添加数组元素,以下依次展开:
//ON_MAIL_NOARGS(VTUI_CLOSE_NOTIFY, MenuSettingDialogC::OnCloseNotify)//这是为消息VTUI_CLOSE_NOTIFY关联一个不带任何参数的映射函数OnCloseNotify()。
{ VTUI_CLOSE_NOTIFY, VTUI_CLOSE_NOTIFY, NOARGS_HANDLER,
(MailHandlerT)(MailHandlerResultT(MailboxC::*)())&MenuSettingDialogC::OnCloseNotify },
//ON_MAIL_WINDOWMAIL(VTUI_MENU_ITEM_HOVERED, MenuSettingDialogC::OnMenuItemHovered) //这是为消息VTUI_MENU_ITEM_HOVERED关联一个带Windows Mail参数的映射函数OnMenuItemHovered, 参数的结构为。
{ VTUI_MENU_ITEM_HOVERED, VTUI_MENU_ITEM_HOVERED, WINDOW_HANDLER,
(MailHandlerT)(MailHandlerResultT(MailboxC::*)(uint32,WindowHandleT,ParamT,ParamT))&MenuSettingDialogC::OnMenuItemHovered },
// ON_MAIL_VOIDPTR(VTUI_OPTION_SELECTED, MenuSettingDialogC::OnOptionSelected)//这是为消息VTUI_OPTION_SELECTED关联一个带空指针参数的映射函数OnMenuItemHovered。
{ VTUI_OPTION_SELECTED, VTUI_OPTION_SELECTED, VOIDPTR_HANDLER, /
(MailHandlerT)(MailHandlerResultT(MailboxC::*)(void*))&MenuSettingDialogC::OnOptionSelected },
以上只是给出了一般性的说明,而要深入追查一个消息是如何跟一个函数关联起来的,还要看以下代码:
union MailMapFunctions
{
MailHandlerT GenericP;
MailHandlerResultT (MailboxC::*WindowMailHandlerT)(uint32 MailMsgId,
WindowHandleT WindowHandle,
ParamT ParamA,
ParamT ParamB);
MailHandlerResultT (MailboxC::*NoArgsMailHandler)(void);
MailHandlerResultT (MailboxC::*VoidPtrMailHandlerT)(void* MailMsgP);
MailHandlerResultT (MailboxC::*MsgIdVoidPtrMailHandlerT)(uint32 MailMsgId, void* MailMsgP);
};
MailHandlerResultT MailboxC::DeliverMail(uint32 MailMsgId, void *MailMsgP)
{
// UIASSERT(MailMsgP != 0);
return FindMailHandler(MailMsgId, MailMsgP);
}
MailHandlerResultT MailboxC::FindMailHandler(uint32 MailMsgId, void *MailMsgP)
{
//传入的是消息ID和映射函数的参数指针,根据函数类型的不同,这个参数可能有不同的解释
uint16 EntryIndex = 0;
MailMapFunctions MailHandlers;//注意这是一个联合体,用于存储映射函数的信息
MailHandlerResultT HandlerResult = CONTINUE_MAIL_DELIVERY;
bool FoundHandler = FALSE;
const MailMapT* MailMap = GetMailMap();
const MailMapEntryT* MailEntryP = NULL;
WindowMailMsgT* WindowMailMsgP = NULL;
//Get the current classes memory map
//Then loop through looking for a match
//If found then return the RESULT back.
//Then get a pointer to the parent's map and loop to the top
do
{
//取得的MailEntry数组的入口
MailEntryP = MailMap->MailEntryP;
EntryIndex = 0;//从元素0开始遍历
while ((MailEntryP[EntryIndex].Args != END_HANDLER) && (!FoundHandler))
{
//由此可以看到,其实mailboxC支持的是多个消息的映射,只不过我们通常把上下限设为一致,在这里我用使用了数组元素的第一和第二个域
if ((MailEntryP[EntryIndex].MailMsgId <= MailMsgId) &&
(MailEntryP[EntryIndex].LastMsgId >= MailMsgId))
{
//取得当前元素的函数指针
MailHandlers.GenericP = MailEntryP[EntryIndex].MailHandlerP;
//根据args成员确定映射函数的类型,并调用相应的函数,使用了数组元素的第三和第四个域
switch (MailEntryP[EntryIndex].Args)
{
case NOARGS_HANDLER:
HandlerResult = (this->*MailHandlers.NoArgsMailHandler)();
break;
case VOIDPTR_HANDLER:
HandlerResult = (this->*MailHandlers.VoidPtrMailHandlerT)(MailMsgP);
break;
case MSGID_VOIDPTR_HANDLER:
HandlerResult =
(this->*MailHandlers.MsgIdVoidPtrMailHandlerT)(MailMsgId, MailMsgP);
break;
case WINDOW_HANDLER:
WindowMailMsgP =(WindowMailMsgT*)MailMsgP;
HandlerResult =
(this->*MailHandlers.WindowMailHandlerT)(MailMsgId,
WindowMailMsgP->WindowHandle, WindowMailMsgP->ParamA,
WindowMailMsgP->ParamB);
break;
}
FoundHandler = TRUE;
}
EntryIndex++;
}
//进入BASE CLASS 的MAP
MailMap = MailMap->BaseMapP;
} while ((MailMap != 0) && (!FoundHandler));
return HandlerResult
}
对于任何需要响应的消息, 调用该类的DeliverMail即可。