深入浅出MFC第2版(PDF格式)-第83部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
nCount = 0;
其实是:
this…》nCount = 0;
基于相同的道理,上例中的EnumObjectsProc 既然是一个成员函数,C++ 编译器也
会为它多准备一个隐藏参数。
好,问题就出在这个隐藏参数。callback 函数是给Windows 调用用的,Windows 并
不经由任何对象调用这个函数,也就无由传递this 指针给callback 函数,于是导至
堆栈中有一个随机变量会成为this 指针,而其结果当然是程序的崩溃了。
要把某个函数用作callback 函数,就必须告诉C++ 编译器,不要放this 指针作为
该函数的最后一个参数。两个方法可以做到这一点:
1。 不要使用类别的成员函数(也就是说,要使用全域函数)做为callback 函数。
2。 使用static 成员函数。也就是在函数前面加上static 修饰词。
第一种作法相当于在C 语言中使用callback 函数。第二种作法比较接近OO 的精神。
我想更进一步提醒你的是,C++ 中的static 成员函数特性是,即使对象还没有产生,
static 成员也已经存在(函数或变量都如此) 。换句话说对象还没有产生之前你已经
可以调用类别的static 函数或使用类别的static 变量了。请参阅第二章。
也就是说,凡声明为static 的东西(不管函数或变量)都并不和对象结合在一起,
它们是类别的一部份,不属于对象。
402
…………………………………………………………Page 465……………………………………………………………
第6章 MFC 程式的生死因果
空闲时间(idle time)的处理:OnIdle
为了让Hello 程序更具体而微地表现一个MFC 应用程序的水准,我打算为它加上空闲
时间(idle time )的处理。
我已经在第1章介绍过了空闲时间,也简介了Win32 程序如何以PeekMessage 「偷闲」。
Microsoft 业已把这个观念落实到CWinApp (不,应该是CWinThread)中。请你回头看
看本章的稍早的「CWinApp::Run 程序生命的活水源头」一节,那一节已经揭露了MFC
消息循环的秘密:
int CWinThread::Run()
{
。。。
for (;;)
{
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
}
。。。 // msg loop
}
}
CThread::OnIdle 做些什么事情呢?CWinApp 改写了OnIdle 函数,CWinApp::OnIdle 又
做些什么事情呢?你可以从THRDCORE。CPP 和APPCORE。CPP 中找到这两个函数的
源代码,源代码可以说明一切。当然基本上我们可以猜测OnIdle 函数中大概是做一些
系统(指的是MFC 本身)的维护工作。这一部份的功能可以说日趋式微,因为低优先
权的执行线程可以替代其角色。
如果你的MFC 程序也想处理idle time,只要改写CWinApp 衍生类别的OnIdle 函数即
可。这个函数的类型如下:
virtual BOOL OnIdle(LONG lCount);
403
…………………………………………………………Page 466……………………………………………………………
第篇 湷觥 FC 程式設計
lCount 是系统传进来的一个值,表示自从上次有消息进来,到现在,OnIdle 已经被调用
了多少次。稍后我将改写Hello 程序,把这个值输出到窗口上,你就可以知道空闲时间
是多么地频繁。lCount 会持续累增,直到CWinThread::Run 的消息循环又获得了一个讯
息,此值才重置为0 。
注意:Jeff Prosise 在他的Programming Windows 95 with MFC 一书第7章谈到OnIdle
函数时,曾经说过有几个消息并不会重置lCount 为0,包括鼠标消息、WM_SYSTIMER 、
WM_PAINT 。不过根据我实测的结果,至少鼠标消息是会的。稍后你可在新版的Hello 程
序移动鼠标,看看lCount 会不会重设为0 。
我如何改写Hello 呢?下面是几个步骤:
1。 在CMyWinApp 中增加OnIdle 函数的声明:
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance(); // 每一个应用程序都应该改写此函数
virtual BOOL OnIdle(LONG lCount); // OnIdle 用来处理空闲时间(idle time)
};
2。 在CMyFrameWnd 中增加一个IdleTimeHandler 函数声明。这么做是因为我希
望在窗口中显示lCount 值, 所以最好的作法就是在OnIdle 中调用
CMyFrameWnd 成员函数,这样才容易获得绘图所需的DC 。
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd(); // constructor
afx_msg void OnPaint(); // for WM_PAINT
afx_msg void OnAbout(); // for WM_MAND (IDM_ABOUT)
void IdleTimeHandler(LONG lCount); // we want it call by CMyWinApp::OnIdle
。。。
};
3。 在HELLO。CPP 中定义CMyWinApp::OnIdle 函数如下:
404
…………………………………………………………Page 467……………………………………………………………
第6章 MFC 程式的生死因果
BOOL CMyWinApp::OnIdle(LONG lCount)
{
CMyFrameWnd* pWnd = (CMyFrameWnd*)m_pMainWnd;
pWnd…》IdleTimeHandler(lCount);
return TRUE;
}
4。 在HELLO。CPP 中定义CMyFrameWnd::IdleTimeHandler 函数如下:
void CMyFrameWnd::IdleTimeHandler(LONG lCount)
{
CString str;
CRect rect(10;10;200;30);
CDC* pDC = new CClientDC(this);
str。Format(〃%010d〃; lCount);
pDC…》DrawText(str; ▭ DT_LEFT | DT_TOP);
}
为了输出lCount,我又动用了三个MFC 类别:CString、CRect 和CDC。前两者非常
简单,只是字符串与四方形结构的一层C++ 包装而且,后者是在Windows 系统中绘图所
必须的DC (Device Context )的一个包装。
新版Hello 执行结果如下。左上角的lCount 以飞快的速度更迭。移动鼠标看看,看
lCount 会不会重置为0 。
405
…………………………………………………………Page 468……………………………………………………………
第篇 湷觥 FC 程式設計
Dialog 与 Control
回忆SDK 程序中的对话框作法:RC 文件中要准备一个对话框的Template,C 程序中要
设计一个对话框函数。MFC 提供的CDialog 已经把对话框的窗口函数设计好了,因此
在MFC 程序中使用对话框非常地简单:
WM_MAND
(IDM_ABOUT)
HELLO。CPP
void CMyFrameWnd::OnAbout()
{
CDialog about(〃AboutBox〃; this);
about。DoModal();
}
HELLO。RC
AboutBox DIALOG DISCARDABLE 34; 22; 147; 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION 〃About Hello〃
{
ICON 〃JJHouRIcon〃;IDC_STATIC;11;17;18;20
LTEXT 〃Hello MFC 4。0〃;IDC_STATIC;40;10;52;8
LTEXT 〃Copyright 1996 Top Studio〃;IDC_STATIC;40;25;100;8
LTEXT 〃J。J。Hou〃;IDC_STATIC;40;40;100;8
DEFPUSHBUTTON 〃OK〃;IDOK;105;7;32;14;WS_GROUP
}
当使用者按下【File/About 】菜单, 根据Message Map 的设定,WM_MAND
(IDM_ABOUT )被送到OnAbout 函数去。我们首先在OnAbout 中产生一个CDialog 物
件,名为about 。CDialog 构造式容许两个参数,第一个参数是对话框的模板资源,第二
个参数是about 对象的主人。由于我们的〃About〃 对话框是如此地简单,不需要改写
CDialog 中的对话框函数,所以接下来直接调用CDialog::DoModal ,对话框就开始运作
了。
406
…………………………………………………………Page 469……………………………………………………………
第6章 MFC 程式的生死因果
通用对话框(mon Dialogs)
有些对话框,例如【File Open 】或【Save As】对话框,出现在每一个程序中的频率是如
此之高,使微软公司不得不面对此一事实。于是,自从Windows 3。1 之后,Windows API
多了一组通用对话框( mon Dialogs ) API 函数, 系统也多了一个对应的
MDLG。DLL (32 位版则为DLG32。DLL )。
MFC 也支持通用对话框,下面是其类别与其类型:
类别 类型
CmonDialog 以下各类别的父类别
CFileDialog File 对话框(Open 或Save As )
CPrintDialog Print 对话框
CFindReplaceDialog Find and Replace 对话框
CColorDialog Color 对话框
CFontDialog Font 对话框
CPageSetupDialog Page Setup 对话框(MFC 4。0 新增)
COleDialog