友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第104部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
。。。
555
…………………………………………………………Page 618……………………………………………………………
第篇 深入 MFC 程式設計
于是,这样的宏:
BEGIN_MESSAGE_MAP(CMyView; CView)
ON_WM_CREATE()
ON_WM_PAINT()
END_MESSAGE_MAP()
便被展开成为这样的码:
const AFX_MSGMAP* CMyView::GetMessageMap() const
{ return &CMyView::messageMap; }
AFX_DATADEF const AFX_MSGMAP CMyView::messageMap =
{ &CView::messageMap; &CMyView::_messageEntries'0' };
const AFX_MSGMAP_ENTRY CMyView::_messageEntries'' =
{
{ WM_CREATE; 0; 0; 0; AfxSig_is;
(AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate };
{ WM_PAINT; 0; 0; 0; AfxSig_vv;
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnPaint };
{0; 0; 0; 0; AfxSig_end; (AFX_PMSG)0 }
};
其中AFX_DATADEF 和AFX_MSG_CALL 又是两个看起来很奇怪的常数。你可以在两
个文件中找到它们的定义:
// in DEVSTUDIOVCMFCINCLUDEAFXVER_。H
#define AFX_DATA
#define AFX_DATADEF
// in DEVSTUDIOVCMFCINCLUDEAFXWIN。H
#define AFX_MSG_CALL
显然它们就像afx_msg 一样(我曾经在第6章的HellpMFC 源代码一出现之后解释
过),都只是个〃intentional placeholder〃 (刻意保留的空间),可能在将来会用到,目前
则为「无物」。
556
…………………………………………………………Page 619……………………………………………………………
第9章 訊息映射與命令繞行
以图表示BEGIN_! K/ON_! K/END_! K 宏的结果为:
CView::messageMap
pBaseMap
CMyView::_messageEntries''
lpEntries
WM_CREATE; 0; 0; 0; AfxSig_is; OnCreate
WM_PAINT; 0; 0; 0; AfxSig_vv; OnPaint CMyView::messageMap
0; 0; 0; 0; AfxSig_end; 0
注意:图中的AfxSig_vv 和AfxSig_is 都代表签名符号(Signature)。这些常数在AFXMSG_。H
中定义,稍后再述。
前面我说过了,所有能够接收消息的类别,都应该衍生自CCmdTarget。那么我们这么推
论应该是合情合理的: 每一个衍生自CCmdTarget 的类别都应该有
DECLARE_/BEGIN_/END_ 宏组?
唔,错了,CWinThread 就没有!
可是这么一来,CWinApp 通往CCmdTarget 的路径不就断掉了吗?呵呵,难道CWinApp
不能跳过CWinThread 直接连上CCmdTarget 吗?看看下面的MFC 源代码:
// in AFXWIN。H
class CWinApp : public CWinThread
{
。。。
DECLARE_MESSAGE_MAP()
};
// in APPCORE。CPP
BEGIN_MESSAGE_MAP(CWinApp; CCmdTarget) //注意第二个参数是CCmdTarget,
//{{AFX_MSG_MAP(CWinApp) //而不是CWinThread。
// Global File mands
557
…………………………………………………………Page 620……………………………………………………………
第篇 深入 MFC 程式設計
ON_MAND(ID_APP_EXIT; OnAppExit)
// MRU most recently used file menu
ON_UPDATE_MAND_UI(ID_FILE_MRU_FILE1; OnUpdateRecentFileMenu)
ON_MAND_EX_RANGE(ID_FILE_MRU_FILE1; ID_FILE_MRU_FILE16; OnOpenRecentFile)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
让我们看看具体的情况。图9…2 就是MFC 的消息映射表。当你的衍生类别使用了
DECLARE_/BEGIN_/END_ 宏,你也就把自己的消息映射表挂上去了…当然是挂在尾
端。
如果没有把BEGIN_MESSAGE_MAP 宏中的两个参数(也就是类别本身及其父类别的
名称)按照规矩来写,可能会发生什么结果呢?消息可能在不应该流向某个类别时流了
过去,在应该被处理时却又跳离了。总之,完美的机制有了破绽。程序没当掉算你幸运!
558
…………………………………………………………Page 621……………………………………………………………
第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 e
0;0;0;0;0;0
CDocument CMyDocument
; ; ; ; ; ; ; ; ; ;
0;0;0;0;0;0 0;0;0;0;0;0
图9…2 MFC 消息映射表 ( 也就是消息流动网)
我们终于了解,Message Map 既可说是一套宏,也可以说是宏展开后所代表的一套
数据结构;甚至也可以说Message Map 是一种动作,这个动作,就是在刚刚所提的资料
结构中寻找与消息相吻合的项目,从而获得消息的处理例程的函数指针。
虽然,C++ 程序员看到多态(Polymorphism),直觉的反应就是虚拟函数,但请注意,
各个Message Map 中的各个同名函数虽有多态的味道,却不是虚拟函数。乍想之下使用
虚拟函数是合理的:你产生一个与窗口有关的C++ 类别,然后为此窗口所可能接收的
任何消息都提供一个对应的虚拟函数。这的确散发着C++ 的味道和对象导向的精神,
但现实与理想之间总是有些距离。
559
…………………………………………………………Page 622……………………………………………………………
第篇 深入 MFC 程式設計
要知道,虚拟函数必须经由一个虚拟函数表(virtual function table ,vtable )实作出来,每
一个子类别必须有它自己的虚拟函数表,其内至少有父类别之虚拟函数表的内容复本(请
参考第2章「类别与对象大解剖」一节)。好哇,虚拟函数表中的每一个项目都是一个
函数指针,价值4 字节,如果基础类别的虚拟函数表有100 个项目,经过10 层继承,
开枝散叶,总共需耗费多少内存在其中?最终,系统会被巨大的额外负担(overhead )
拖垮!
这就是为什么MFC 采用独特的消息映射机制而不采用虚拟函数的原因。
米诺托斯 (Minotauros)与西修斯 (Theseus)
截至目前我还有一些细节没有交待清楚,像是消息的比对动作、消息处理例程的调用动
作、以及参数的传递等等,但至少现在可以先继续进行下去,我的目标瞄准消息唧筒(叫
邦浦也可以啦)。
窗口接收消息后,是谁把消息唧进消息映射网中?是谁决定消息该直直往父映射表走
去?还是拐向另一条路(请回头看看图9…2 )?消息的绕行路线,以及MFC 的消息唧
筒的设计,活像是米诺托斯的迷宫。不过别担心,我将扮演西修斯,让你免遭毒手。
米诺托斯(Minotauros ),希腊神话里牛头人身的怪兽,为克里特岛国王迈诺斯之妻所生。
迈诺斯造迷宫将米诺托斯藏于其中,每有人误入迷宫即遭吞噬。怪兽后为雅典王子西修
斯(Theseus )所杀。
MFC 2。5 (注意,是2。5 而非4。x)曾经在WinMain 的第一个重要动作AfxWinInit
之中, 自动为程序注册四个Windows 窗口类别, 并且把窗口函数一致设为
AfxWndProc :
560
…………………………………………………………Page 623……………………………………………………………
第9章 訊息映射與命令繞行
//in APPINIT。CPP ( )
MFC 2。5
BOOL AFXAPI AfxWinInit (HINSTANCE hInstance; HINSTANCE hPrevInstance;
LPSTR lpCmdLine; int nCmdShow)
{
。。。
// register basic WndClasses (以下开始注册窗口类别)
WNDCLASS wndcls;
wndcls。lpfnWndProc = AfxWndProc;
// Child windows no brush; no icon; safest default class styles
。。。
wndcls。lpszClassName = _afxWnd;
if (! ::RegisterClass(&wndcls))
return FALSE;
// Control bar windows
。。。
wndcls。lpszClassName = _afxWndControlBar;
if (! ::RegisterClass(&wndcls))
return FALSE;
// MDI Frame window (also used for splitter window)
。。。
if (!RegisterWithIcon (&wndcls; _afxWndMDIFrame; AFX_IDI_STD_MDIFRAME))
return FALSE;
// SDI Frame or MDI Child windows or views normal colors
。。。
if (!RegisterWithIcon (&wndcls; _afxWndFrameOrView; AFX_IDI_STD_FRAME))
return FALSE;
。。。
}
下面是AfxWndProc 的内容:
// in WINCORE。CPP ( )
MFC 2。5
LRESULT CALLBACK AFX_EXPORT
AfxWndProc (HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam)
{
CWnd* pWnd;
pWnd = CWnd::FromHandlePermanent(hWnd);
ASSERT(pWnd != NULL);
ASSERT(pWnd…》m_hWnd == hWnd);
561
…………………………………………………………Page 624……………………………………………………………
第篇 深入 MFC 程式設計
LRESULT lResult = _AfxCallWndProc (pWnd; hWnd; message; wParam; lParam);
return lResult;
}
// Official way to send message to a CWnd
LRESULT PASCAL _AfxCallWndProc (CWnd* pWnd; HWND hWnd; UINT message;
WPARAM wParam; LPARAM lParam)
{
LRESULT lResult;
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!