友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第54部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
m_pMainWnd…》UpdateWindow();
2 8
AfxWinInit(。。。);
return TRUE;
3 }
pApp…》InitApplication();
pApp…》InitInstance();
4
CMyFrameWnd::CMyFrameWnd()
nReturnCode = pApp…》Run();
9 {
6
Create(NULL; 〃Hello MFC〃; 。。。;
AfxWinTerm();
〃MainMenu〃);
}
CWinApp::Run }
CWinApp::Run
CWinThread::Run void CMyFrameWnd::OnPaint() { 。。。 }
do { void CMyFrameWnd::OnAbout() { 。。。 }
do {
::GetMessage(&msg;。。。);
::GetMessage(&msg;。。。);
PreTranslateMessage{&msg);
PreTranslateMessage{&msg); BEGIN_MESSAGE_MAP(CMyFrameWnd; CFrameWnd)
::TranslateMessage(&msg);
::TranslateMessage(&msg);
::DispatchMessage(&msg); ON_MAND(IDM_ABOUT; OnAbout)
::DispatchMessage(&msg);
。。。 ON_WM_PAINT()
。。。
} while (::PeekMessage(。。。)); END_MESSAGE_MAP()
} while (::PeekMessage(。。。));
DefWindowProc
DefWindowProc AfxWndProc
hooking and subclassing AfxWndProc
(please see chap9; p。560;
“Minotauros and Theseus”)
Hello 程序进行到这里,窗口类别注册好了,窗口诞生并显示出来了,UpdateWindow 被
调用,使得消息队列中出现了一个WM_PAINT 消息,等待被处理。现在,执行的脚步到
达pApp …》Run 。
稍早我说过了,pApp 指向CMyWinApp 对象(也就是本例的theApp ),所以,当程序
调用:
pApp…》Run();
相当于调用:
CMyWinApp::Run();
390
…………………………………………………………Page 453……………………………………………………………
第6章 MFC 程式的生死因果
要知道,CMyWinApp 继承自CWinApp,而Run 又是CWinApp 的一个虚拟函数。我们
并没有改写它(大部份情况下不需改写它),所以上述动作相当于调用:
CWinApp::Run();
其源代码出现在APPCORE。CPP 中:
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation; but has no main window!
TRACE0(〃Warning: m_pMainWnd is NULL in CWinApp::Run quitting
application。n〃);
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
32 位MFC 与16 位MFC 的巨大差异在于CWinApp 与CCmdTarget 之间多出了一
个CWinThread,事情变得稍微复杂一些。CWinThread 定义于THRDCORE。CPP :
int CWinThread::Run()
{
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received。
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&m_msgCur; NULL; NULL; NULL; PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume 〃no idle〃 state
}
// phase2: pump messages while available
do
{
// pump message; but quit on WM_QUIT
391
…………………………………………………………Page 454……………………………………………………………
第篇 湷觥 FC 程式設計
if (!PumpMessage ())
return ExitInstance();
// reset 〃no idle〃 state after pumping 〃normal〃 message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur; NULL; NULL; NULL; PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur; NULL; NULL; NULL))
{
return FALSE;
}
// process this message
if (m_msgCur。message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
获得的消息如何交给适当的例程去处理呢?SDK 程序的作法是调用DispatchMessage ,把
消息丢给窗口函数;MFC 也是如此。但我们并未在Hello 程序中提供任何窗口函数,是
的,窗口函数事实上由MFC 提供。回头看看前面AfxEndDeferRegisterClass 源代码,它
在注册四种窗口类别之前已经指定窗口函数为:
wndcls。lpfnWndProc = DefWindowProc;
392
…………………………………………………………Page 455……………………………………………………………
第6章 MFC 程式的生死因果
注意,虽然窗口函数被指定为DefWindowProc 成员函数,但事实上消息并不是被唧往该
处,而是一个名为AfxWndProc 的全域函数去。这其中牵扯到MFC 暗中做了大挪移的
手脚(利用hook 和subclassing),我将在第9章详细讨论这个「乾坤大挪移」。
你看,WinMain 已由MFC 提供,窗口类别已由MFC 注册完成、连窗口函数也都由MFC
提供。那么我们(程序员)如何为特定的消息设计特定的处理例程?MFC 应用程序对讯
息的辨识与判别是采用所谓的「Message Map 机制」。
393
…………………………………………………………Page 456……………………………………………………………
第篇 湷觥 FC 程式設計
把消息与处理函数串接在一起:Message Map 机制
HELLO。CPP
1 CMyWinApp theApp; // application object
BOOL CMyWinApp::InitInstance()
WINMAIN。CPP
{
5
int AFXAPI AfxWinMain (。。。) m_pMainWnd = new CMyFrameWnd();
{ m_pMainWnd…》ShowWindow(m_nCmdShow);
7
CWinApp* pApp = AfxGetApp(); m_pMainWnd…》UpdateWindow();
8
return TRUE;
2 }
AfxWinInit(。。。);
3 CMyFrameWnd::CMyFrameWnd()
pApp…》InitApplication();
pApp…》InitInstance(); {
4
6
nReturnCode = pApp…》Run(); Create(NULL; 〃Hello MFC〃; 。。。;
9
〃MainMenu〃);
AfxWinTerm(); }
}
void CMyFrameWnd::OnPaint() { 。。。 } 11
AfxWndProc void CMyFrameWnd::OnAbout() { 。。。 }
m m
e e
s s BEGIN_MESSAGE_MAP(CMyFrameWnd; CFrameWnd)
s s
a a
WM_PAINT WM_PAINT
g g ON_MAND(IDM_ABOUT; OnAbout)
e
r e
o ON_WM_PAINT()
u m
t a
i
n
10 END_MESSAGE_MAP()
g p
。
。
。
基本上Message Map 机制是为了提供更方便的程序接口(例如宏或表格),让程序员
很方便就可以建立起消息与处理例程的对应关系。这并不是什么新发明,我在第1章示
范了一种风格简明的SDK 程序写法,就已经展现出这种精神。
MFC 提供给应用程序使用的「很方便的接口」是两组宏。以Hello 的主窗口为例,
第一个动作是在HELLO。H 的CMyFrameWnd 加上DECLARE_MESSAGE_MAP :
394
…………………………………………………………Page 457……………………………………………………………
第6章 MFC 程式的生死因果
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd();
afx_msg void OnPaint();
afx_msg void OnAbout();
DECLARE_MESSAGE_MAP()
};
第二个动作是在HELLO。CPP 的任何位置(当然不能在函数之内)使用宏如下:
BEGIN_MESSAGE_MAP(CMyFrameWnd; CFrameWnd)
ON_WM_PAINT()
ON_MAND(IDM_ABOUT; OnAbout)
END_MESSAGE_MAP()
这么一来就把消息WM_PAINT 导到OnPaint 函数, 把WM_MAND
(IDM_ABOUT )导到OnAbout 函数去了。但是,单凭一个ON_WM_PAINT 宏,没
有任何参数,如何使WM_PAINT 流到OnPaint 函数呢?
MFC 把消息主要分为三大类,Message Map 机制中对于消息与函数间的对映关系也明定
以下三种:
■ 标准Windows 消息(WM_xxx )的对映规则:
宏名称 对映消息 消息处理函数(名称已由系统预设)
ON_WM_CHAR WM_CHAR OnChar
ON_WM_CLOSE WM_CLOSE OnClose
ON_WM_CREATE WM_CREATE OnCreate
ON_WM_DESTROY WM_DESTROY OnDestroy
ON_WM_LBUTTONDOWN WM_LBUTTONDOWN OnLButtonDown
ON_WM_LBUTTONUP WM_LBUTTONUP OnLButtonUp
ON_WM_MOUSEMOVE WM_MOUSEMOVE OnMouseMove
ON_WM_PAINT WM_PAINT OnPaint
。。。
395
…………………………………………………………Page 458……………………………………………………………
第篇 湷觥 FC 程式設計
■ 命令消息(WM_MAND)的一般性对映规则是:
ON_MAND(;)
例如:
ON_MAND(IDM_ABOUT; OnAbout)
ON_MAND(IDM_FILENEW; OnFileNew)
ON_MAND(IDM_FILEOPEN; OnFileOpen)
ON_MAND(IDM_FILESAVE; OnFileSave)
■ 「Notification 消息」(由控制组件产生,例如BN_xxx )的对映机制的宏分
为好几种(因为控制组件本就分为好几种),以下各举一例做代表:
控制组件 宏名称 消息处理函数
Button ON_BN_CLICKED(;) memberFxn
boBox ON_CBN_DBLCLK(;) memberFxn
Edit ON_EN_SETFOCUS(;) memberFxn
ListBox ON_LBN_DBLCLK(;) memberFxn
各个消息处理函数均应以afx_msg void 为函数型式。
为什么经过这样的宏之后,消息就会自动流往指定的函数去呢?谜底在于Message Ma
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!