友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第108部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
图9…5 画出FrameWnd 窗口收到命令消息后的四个尝试路径。第3章曾经以一个简单的
DOS 程序仿真出这样的绕行路线。
576
…………………………………………………………Page 639……………………………………………………………
第9章 訊息映射與命令繞行
CWinThread CWinApp CMyWinApp
; ; ; ; ; ; ; ; ; ;
0;0;0;0;0;0 0;0;0;0;0;0
CView CMyView
m
e
CCmdTarget CWnd CFrameWnd CMyFrameWnd
s
; ; ; ; ; ; ; ; ; ;
s
0;0;0;0;0;0 0;0;0;0;0;0 a
; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
; ; ; ; ; g
0;0;0;0;0;0 0;0;0;0;0;0 0;0;0;0;0;0 WM_MAND e
0;0;0;0;0;0
CDocument CMyDocument
当CMyFrameWnd 收到WM_MAND ,
消息唧筒尝试数种绕行路线,使命令消息有
; ; ; ; ; ; ; ; ; ; 机会经过任何一个类别。图中的
0;0;0;0;0;0 0;0;0;0;0;0 即绕行的优先次序(请参考图9…4 )。
图9…5 FrameWnd 窗口收到命令消息后的四个尝试路径。第 3 章曾经以一
个简单的DOS 程序仿真出这样的绕行路线。
OnCmdMsg 是各类别专门用来对付命令消息的函数。每一个「可接受命令消息之对象」
(mand Target )在处理命令消息时都会(都应该)遵循一个游戏规则:调用另一个
目标类别的OnCmdMsg 。这才能够将命令消息传送下去。如果说AfxWndProc 是消息流
动的「唧筒」,各类别的OnCmdMsg 就是消息流动的「转辙器」。
以下我举一个具体例子。假设命令消息从Scribble 的【Edit/Clear All 】发出,其处理常
式位在CScribbleDoc,下面是这个命令消息的流浪过程:
577
…………………………………………………………Page 640……………………………………………………………
第篇 深入 MFC 程式設計
1。 MDI 主窗口( CMDIFrameWnd ) 收到命令消息WM_MAND, 其ID 为
ID_EDIT_CLEAR_ALL 。
2。 MDI 主窗口把命令消息交给目前作用中的MDI 子窗口(CMDIChildWnd)。
3。 MDI 子窗口给它自己的子窗口(也就是View )一个机会。
4。 View 检查自己的Message Map 。
5。 View 发现没有任何处理例程可以处理此命令消息,只好把它传给Document 。
6。 Document 检查自己的Message Map ,它发现了一个吻合项:
BEGIN_MESSAGE_MAP(CScribbleDoc; CDocument)
ON_MAND(ID_EDIT_CLEAR_ALL; OnEditClearAll)
。。。
END_MESSAGE_MAP()
于是调用该函数,命令消息的流动路线也告终止。
如果上述的步骤6 仍没有找到处理函数,那么就:
7。 Document 把这个命令消息再送到Document Template 对象去。
8。 还是没被处理,于是命令消息回到View 。
9。 View 没有处理,于是又回给MDI 子窗口本身。
10。 传给CWinApp 对象…无主消息的终极归属。
图9…6 是构成「消息邦浦」之各个函数的调用次序。此图可以对前面所列之各个
源代码组织出一个大局观来。
578
…………………………………………………………Page 641……………………………………………………………
第9章 訊息映射與命令繞行
AfxWndProc
AfxWndProc
message
AfxCallWndProc
AfxCallWndProc
CWnd::WindowProc
CWnd::WindowProc
CWnd::OnWndMsg
CWnd::OnWndMsg ::DefWindowProc
CWnd::DefWindowProc ::DefWindowProc
CWnd::DefWindowProc
AfxFindMessageEntry
WM_MAND regular window message AfxFindMessageEntry
CFrameWnd::Onmand
CFrameWnd::Onmand
msg handler
CWnd::Onmand
CWnd::Onmand
CFrameWnd::OnCmdMsg
CFrameWnd::OnCmdMsg
CView::OnCmdMsg
CView::OnCmdMsg CWnd::OnCmdMsg
CWnd::OnCmdMsg
(CCmdTarget::OnCmdMsg)
(CCmdTarget::OnCmdMsg)
CWnd::OnCmdMsg CDocument::OnCmdMsg
CWnd::OnCmdMsg CDocument::OnCmdMsg CWinApp::OnCmdMsg
CWinApp::OnCmdMsg
(CCmdTarget::OnCmdMsg)
(CCmdTarget::OnCmdMsg) (CCmdTarget::OnCmdMag)
(CCmdTarget::OnCmdMag)
CCmdTarget::OnCmdMsg
CCmdTarget::OnCmdMsg
DispatchCmdMsg
DispatchCmdMsg
DispatchCmdMsg DispatchCmdMsg
DispatchCmdMsg DispatchCmdMsg
DispatchCmdMsg
DispatchCmdMsg
msg handler FALSE FALSE FALSE
FALSE msg handler msg handler
msg handler
图9…6 构成 「消息邦浦」之各个函数的调用次序
579
…………………………………………………………Page 642……………………………………………………………
第篇 深入 MFC 程式設計
罗塞达碑石:AfxSig_xx 的奥秘
大架构建立起来了,但我还没有很仔细地解释在消息映射「网」中的_messageEntries ''
数组内容。为什么消息经由推动引擎(上一节谈的那整套家伙)推过这些数组,就可以
找到它的处理例程?
Paul DiLascia 在他的文章(! ¨ Meandering Through the Maze of MFC Message and mand
Routing! ¨,Microsoft Systems Journal ,1995/07)中形容这些数组之内一笔一笔的记录像
是罗塞达碑石,呵呵,就靠它们揭开消息映射的最后谜底了。
罗塞达碑石(Rosetta Stone ),1799 年拿破仑远征埃及时,由一名官员在尼罗河口罗塞
达发现,揭开了古埃及象形文字之谜。石碑是黑色玄武岩,高114 公分,厚28 公分,宽
72 公分。经法国学者Jean…Francois Champollion 研究后,世人因得顺利研读古埃及文献。
消息映射表的每一笔记录是这样的形式:
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
内中包括一个Windows 消息、其控制组件ID 以及通告码(notification code ,对消息的
更多描述,例如EN_CHANGED 或CBN_DROPDIOWN 等)、一个签名记号、以及一个
CCmdTarget 衍生类别的成员函数。任何一个ON_ 宏会把这六个项目初始化起来。例
如:
#define ON_WM_CREATE ()
{ WM_CREATE; 0; 0; 0; AfxSig_is;
(AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate };
你看到了可怕的类型转换动作,这完全是为了保持类型安全(type…safe )。
580
…………………………………………………………Page 643……………………………………………………………
第9章 訊息映射與命令繞行
有一个很莫名其妙的东西:AfxSig_ 。要了解它作什么用,你得先停下来几分钟,想想另
一个问题:当上一节的推动引擎比对消息并发现吻合之后,就调用对应的处理例程,但
它怎么知道要交给消息处理例程哪些参数呢?要知道,不同的消息处理例程需要不同的
参数(包括个数和类型),而其函数指针(AFX_PMSG )却都被定义为这付德行:
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
这么简陋的信息无法表现应该传递什么样的参数,而这正是AfxSig_ 要贡献的地方。当
推动引擎比对完成,欲调用某个消息处理例程lpEntry…》pfn 时,动作是这样子地(出现
在CWnd::OnWndMsg 和DispatchCmdMsg 中):
union MessageMapFunctions mmf;
mmf。pfn = lpEntry…》pfn;
switch (lpEntry…》nSig)
{
case AfxSig_is:
lResult = (this…》*mmf。pfn_is)((LPTSTR)lParam);
break;
case AfxSig_lwl:
lResult = (this…》*mmf。pfn_lwl)(wParam; lParam);
break;
case AfxSig_vv:
(this…》*mmf。pfn_vv)();
break;
。。。
}
注意两样东西:MessageMapFunctions 和AfxSig_ 。AfxSig_ 定义于AFXMSG_。H 档:
enum AfxSig
{
AfxSig_end = 0; // 'marks end of message map'
AfxSig_bD; // BOOL (CDC*)
AfxSig_bb; // BOOL (BOOL)
AfxSig_bWww; // BOOL (CWnd*; UINT; UINT)
AfxSig_hDWw; // HBRUSH (CDC*; CWnd*; UINT)
AfxSig_hDw; // HBRUSH (CDC*; UINT)
581
…………………………………………………………Page 644……………………………………………………………
第篇 深入 MFC 程式設計
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!