2024年3月6日发(作者:)

01章

用向导创建文档窗口

MFC教程是小雅最怕写的内容,除了水平问题之外,还有一点就是,MFC将许多程序封装起来,使众多程序员在编程时,即使编出了程序,也不知究竟为什么这样就可以了。这一现象使小雅想到当今的Java程序员,许多人在开发外包项目时,因为整个FrameWork已经建成,自己只是担当其中一部分功能,出了BUG之后,找原因非常困难。

在SDK教程中,小雅将各种功能进行详细解说,而MFC教程中,只是用例子来讲解MFC的原理,学会原理之后,就会感到MFC比SDK编程省了许多事。小雅在SDK教程中,为了讲明API功能,用的方法都是C语言的方法,而MFC用的全是C++的内容,因此,学习MFC必须对C++有较深的了解,不会C语言、C++语言而能够学会MFC的人只能是吹牛。

小雅的“C语言教程”和“C++语言教程”都是按照标准C/C++来写的,在Unix、Linux、Windows都适用,这是很重要的基础。SDK教程主要是让初学者明白WindowsAPI的功能,以及程序的实现方法,基本上是“C语言 + API”,而MFC基本上是“C++ + API”。SDK编程和MFC是可以相互转化,相互调用的,因为MFC封装了绝大部分API,但不是所有的API函数。注意:SDK并不是只能用C语言来写,用C++一样编程,小雅在作教程时为了把内容说清楚,同时兼顾不会C++的人员才使用C语言。

对于初学者,小雅建议在C/C++的基础上用MFC来编程,一是省去了很多编程,二是用上了C++的面向对象编程的思想。当今要找一个C++的项目来一边做一边学是很困难的。小雅自己也一直想找一个C++的工作而未能如愿。通过学习MFC再反过来加强一下学的不是很深的C++,不是很好的一个方法吗?不过,MFC和SDK与C/C++不同点在消息事件处理方面,初学者要学会这一内容。

关于“”有“托管”和“非托管”二种编程方式,小雅的SDK教程和MFC教程全部是“非托管”编程,也就是传统编程。微软自己说“托管”方式是为传统编程人员的过渡,既然是过渡的产物,小雅也就不去浪费时间精力去写这种教程了,如果想用“托管”编程,还不如直接用C#或。

一、创建一个单文档窗口

MFC编程很大程度上要借助于集成环境中的各种工具,下面我们也通过工具来生成一个基本的窗口程序,再通过这个程序来了解MFC的各主要的类、基本类,学会图标、菜单、资源视图、类视图、解决方案资源管理器、对象浏览器的使用。创建一个单文档窗口的步骤是:

用菜单的“文件”→“新建”→“项目”打开“新建项目”对话框。

在“新建项目”对话框中分别选择设置①类型为“Visual C++ 项目”、②“MFC”、③模板为“MFC应用程序”、④名称为“Test”⑤位置为“C:MFC”。其中④和⑤可自由设定。最后按“确定”按钮进入“MFC应用程序向导”对话框。

在“MFC应用程序向导”对话框中,①选择菜单“应用程序类型”、②选择“单文档”选项。最后按“完成”按钮结束。

二、生成后的文件的作用

生成的文件的作用可以在便可知道。当然,后面章节中小雅将对每一部分详细剖析。

================================================================================

MICROSOFT 基础类库: Test 项目概述

===============================================================================

应用程序向导已为您创建了此 Test 应用程序。此应用程序不仅介绍了使用 Microsoft 基

础类的基本知识,而且是编写应用程序的起点。

此文件包含组成 Test 应用程序的每个文件的内容摘要。

这是使用“应用程序向导”生成的 VC++ 项目的主项目文件。

它包含有关生成文件的 Visual C++ 版本的信息,以及有关用“应用程序向导”所选择

的平台、配置和项目功能的信息。

Test.h

这是应用程序的主头文件。 它包含其他项目特定的头文件(包括 Resource.h),并声

明 CTestApp 应用程序类。

这是包含应用程序类 CTestApp 的主应用程序源文件。

这是程序使用的所有 Microsoft Windows 资源的列表。它包含存储在 RES 子目录中的图

标、位图和光标。 可直接在 Microsoft Visual C++ 中编辑此文件。 项目资源包含在

2052 中。

这是一个图标文件,用作应用程序的图标。 此图标包含在主资源文件 中。

2

此文件包含不由 Microsoft Visual C++ 编辑的资源。 应将所有不能由资源编辑器编辑的

资源放在此文件中。

/////////////////////////////////////////////////////////////////////////////

对于主框架窗口:

项目将包含标准的 MFC 界面。

MainFrm.h,

这些文件包含框架类 CMainFrame,该类派生自 CFrameWnd 并控制所有的 SDI 框架功能。

此位图文件用于创建工具栏的平铺图像。初始工具栏和状态栏在 CMainFrame 类中构造。

使用资源编辑器编辑此工具栏位图,并更新 中的 IDR_MAINFRAME TOOLBAR 数组

以添加工具栏按钮。

/////////////////////////////////////////////////////////////////////////////

应用程序向导将创建一种文档类型和一个视图:

TestDoc.h、 - 文档

这些文件包含 CTestDoc 类。 编辑这些文件以添加特殊的文档数据并实现文件的保存和

加载(通过 CTestDoc::Serialize)。

TestView.h、 - 文档视图

这些文件包含 CTestView 类。

CTestView 对象用于查看 CTestDoc 对象。

/////////////////////////////////////////////////////////////////////////////

其他功能:

ActiveX 控件

应用程序支持使用 ActiveX 控件。

打印支持和打印预览支持

应用程序向导已生成了一些代码,通过从 MFC 库调用 CView 类中的成员函数来处理打印、

打印设置和打印预览命令。

/////////////////////////////////////////////////////////////////////////////

其他标准文件:

StdAfx.h、

这些文件用于生成名为 的预编译头文件(PCH)和名为 的预编译类型

文件。

Resource.h

这是标准头文件,它定义新资源 ID。

Microsoft Visual C++ 将读取并更新此文件。

/////////////////////////////////////////////////////////////////////////////

其他说明:

应用程序向导使用“TODO:” 来指示应添加或自定义的源代码部分。

如果应用程序在共享 DLL 中使用 MFC,且应用程序使用的语言不是操作系统的当前语言,则需

要从 Microsoft Visual C++ 光盘上 WinSystem 目录下将相应的本地化资源

复制到计算机的 system 或 system32 目录下,并将其重命名为 。 (“XXX”代表

语言缩写。 例如, 包含翻译成德语的资源。) 如果不这样做,应用程序的某

些 UI 元素将保留为操作系统的语言。

02章

用向导创建对话框窗口

一、应用程序的窗口种类

Windows应用程序的窗口类型主要有二种即文档类型(Document)和对话框类型(Dialog),通常说的表单类型(Form)是介于这2种类型之间,主要是对话框类型。、C#等主要就是这种表单类型的窗口,实现起来相当简单。

文档类型的应用程序的界面,大部分区域用于文字或画图等读写操作,常见的有Word、Excel、写字板等。文档类型的窗口又分单文档和多文档2种类型。所谓

多文档类型就是在一个窗口框架内有多个子窗口,常见的有Word、Excel、当前编写VC++的IDE等。单文档类型当然就不可能有多个子窗口了,常见的有附件中的写字板、画图等。文档类型的窗口的编程相对对话框来说要复杂得多,是我们学习的主要对象。

对话框类型的界面中使用了很多控件,输入输出主要是针对控件,常见的有计算器、音量控制等。编程相对比较简单,另外公共对话框也是这种类型,不过不需要任何编程,因为这些是操作系统的东西,只调用就可以了。对话框类型是日常我们编程用的最多的,也就是表单类型。另外,对话框类型也可以象文档类型一样进行文字、画图等处理,而文档类型窗口中同样也可以使用控件。

对于文档窗口一般可以改变大小,并且有滚动条,背景多数为白色。而对话框窗口的大小一般是固定的,背景一般是淡灰色,没有滚动条,通常有OK、Cancel等按钮。要注意,对话框窗口有模态和非模态2种类型。所谓模态对话框就是当前对话框不关闭,就不能切换到调用该对话框的父窗口;而所谓非模态窗口就是不关闭当前对话框,也同样可以切换到调用该对话框的父窗口。绝大多数对话框都是模态方式,非模态方式比较少,例如集成环境中按Ctrl+F弹出的“查找”对话框就是非模态方式。

二、创建一个基于对话框的窗口

在上一章生成单文档应用程序的最后一步中,选择“基于对话框”后再按“完成”按钮。

三、阅读原程序的方法

下面,我们要学会用用四种工具来查看或修改生成好的程序。①解决方案资源管理器(以前称为项目管理器);②类视图;③资源视图;④属性。

“解决方案资源管理器”中显示项目中的所有文件,如果要删除一个既存的程序,或添加一个新程序或头文件等,都是利用这个工具来完成。

“资源视图”中包含菜单、鼠标光标、对话框、图标、工具条、字符串常量表、版本信息表、快捷键、HTML等,在“解决方案资源管理器”中鼠标双击后缀为rc的文件便可打开“资源视图”。添加、修改、删除“资源”都是在“资源视图”中操作。

这些“资源”文件其实都是文本文件,内容基本上是资源的定义(图标等除外),完全可以直接手动编写,但手动编写既容易出错,也不方便。另外,对话框的设计还需要和“属性”工具一起使用,这些都是微软Visual Studio集成工具的基本使用常识,MFC教程中不再详细叙述。

“类视图”是学习MFC最重要的工具,小雅认为初学者要学会或者说习惯这个工具的使用。一个很简单的程序,经过MFC生成之后,便是一个“庞大”的程序,初学者如果一行一行地去读,往往头晕目眩,越看越烦。而通过“类视图”从整体上来分析,能很好地把握整个的设计思路,通过“类视图”能很准确地定位到你想编程的“位置”。例如,添加类成员变量或成员函数,消息处理等,利用工具,多个地方的程序将被同进更新。

通过“类视图”还可以很方便地看到父类的全部信息,从而了解当前的类还有哪些功能可以使用。同时可以看到想相关类的全部信息。这比Java的集成环境(如:Websphere等)不知要方便多少倍。

“属性”使用该窗口查看和更改位于编辑器和设计器中的选定对象的设计时属性及事件。也可以使用“属性”窗口编辑和查看文件、项目及解决方案属性。 “属性”窗口显示编辑字段的不同类型,这取决于特定属性的需要。这些编辑字段包括编辑框、下拉列表以及到自定义编辑器对话框的链接。属性以灰色显示且是只读的。

元素

对象名

按分类顺序

按字母顺序

属性

图形 说明

列出当前选定的一个或多个对象。只有活动编辑器或设计器中的对象可见。

当选择多个对象时,只出现所有选定对象的通用属性。

按类别列出选定对象的所有属性及属性值。可以折叠类别以减少可见属性数。展开或折叠类别时,可以在类别名左边看到加号 (+) 或减号 (-)。类别按字母顺序列出。

按字母顺序对选定对象的所有设计时属性和事件排序。若要编辑可用的属性,请在它右边的单元格中单击并输入更改内容。

显示对象的属性。很多对象的事件也可以使用“属性”窗口来查看。

显示对象的事件。仅当窗体或控件设计器在一个 Visual C# 项目或 C++ 项事件

目托管扩展的上下文中处于活动状态时,此“属性”窗口工具栏控件才可用。

列出所有 Windows 消息。为选定类的消息添加或删除指定处理函数。此“属VC消息

性”窗口工具栏控件仅当“类视图”在 Visual C++ 项目的上下文中为活动窗口时才可用。

为选定类列出所有虚函数并允许添加或删除重写函数。此“属性”窗口工具VC重写

栏控件仅当“类视图”在 Visual C++ 项目的上下文中为活动窗口时才可用。

显示选定项的“属性页”对话框。“属性页”显示“属性”窗口中的可用属属性页

性的子集、同集或超集。使用该按钮可查看和编辑与项目的活动配置相关的属性。

“说明”窗格

N/A

显示属性的属性类型和简短说明。可以使用快捷菜单上的“说明”命令关闭和打开属性的说明。

03章

初始大小、标题和图标的改变

一、直接修改现有图标

在上一章生成的基础上,打开资源视图,选择唯一的图标“IDR_MAINFRAME”双击,然后将图标“擦除”,画一个自己喜欢的图形并保存。“Ctrl+F5”重新编译生成运行,你将会和小雅一样惊奇地发现,运行出来的程序的图标并没有改变。

小雅是个喜欢钻牛角尖的人,将源程序前前后后地研究了很长时间,未得其解,只好不耻下问。身边的人问了、论坛上也发贴了,仍未找到答案。不免有几句牢骚,中国许多人在招聘时个个都说自己能力很强,问他会不会VC++,他会举出很多做过VC++的项目,进了单位之后,让他编一个很简单的程序,费了九牛二虎之力生成一堆废代码。如果给他一个sample的话,倒能发挥发挥。

这种现象决不是小雅有意要攻击哪个人,在国内如此,在国外更是如此。而许多编程人员,明明技术很差,却个个自以为了不起,一点没有想学的念头。不仅如此,许多人都象穷疯了一样,都想通过一两次生意、或一两年奋斗来“暴发”一下。这种好大喜功的心理在中国很普遍,国家领导也将经济增长的数目作为政绩,而不注重实实在在的技术、生产能力,靠出卖廉价劳动带来的经济增长,这有什么好吹的。

那么原因出在哪儿呢,原因出在MFC生成的图标上,我们自己新建一个图标,无论怎样画大小也只有1K左右,而MFC生成的图标竟有22K之多。小雅将MFC生成的图标一个象素一个象素地在新建的图标上画了一遍,大小也只有1K左右,新画的图标在Windows的资源管理器里面,无论大图标还是小图标,画错了的地方都能立即显示出来,而MFC生成的图标,无论怎样修改,在Windows的资源管理器里面,无论大图标还是小图标都没有任何改变。

小雅知识有限,对图象方面了解不多,记得看过一篇文章,介绍如何通过调色板控制BMP,使其颜色不能被修改。图标也应该有类似的功能,小雅既然不会,就介绍一下更换图标的笨方法。这个笨办法对第1章和第2章生成的程序都适用。

1. 在资源视图中“Icon”下选择“IDR_MAINFRAME”图标,然后用“Delete”键删除。

2. 在Windows的资源管理器里面,打开当前项目下的“res”子目录,删除相应的图标文件。

3. 在资源视图中“~.rc”下,用右键菜单的“插入资源(A)...”,然后在对话框中选择“Icon”新建一个图标。

4. 在属性工具中,将新建的图标的ID改为“IDR_MAINFRAME”(默认为“IDI_ICON1”)。

5. 在属性工具中,将新建的图标的Filename改为上面删除的文件名。注意应保存在“res”子目录下。

如果你事先已经有一个自己的图标,那最简单了,在VC++中什么也不需要修改,直接在资源管理器里面将那个22K的图标文件删除,然后用你自己的图标换成被删除的文件名。

二、改变窗口的标题和初始大小

在资源视图中打开“String Table”,IDR_MAINFRAME的值为“nt”,将之改为:“yaern小雅nt”,窗口的标题就改完了。

初始大小的更改,对于第2章生成的基于对话框的程序来说很简单,只要在资源视图中选择主窗口后打开,直接用鼠标改变大小就可以了。

对于第1章生成的文档类程序不忙于修改,先用类视图来看一下MFC生成的单文档程序和对话框程序的类有什么相同和不同。

单文档 对话框

“关于”对话框类。无CAboutDlg “关于”对话框类。无须修改。 CAboutDlg

须修改。

CTest2App

主应用程序类,调用框架类、文档主应用程序类,调用主CTest3App

类、视图类。无须修改。 对话框类。无须修改。

主框架类,即主窗口。很少需要修CMainFrame

改。

CTest2Doc

文档类,即存放数据主要在这儿编写代码。 Ctest3Dlg 主对话框类。

视图类,即用于屏幕显示的类,文CTest2view 字显示和画图主要在这儿编写代码。

修改窗口的初始大小在CMainFrame类的PreCreateWindow()成员函数中,通过修改 CREATESTRUCT cs 来修改窗口类或样式。

typedef struct tagCREATESTRUCTW {

LPVOID lpCreateParams;

//指向创建窗口用的数据的指针

HANDLE hInstance;

//窗口的实例句柄

HMENU hMenu;

//窗口的菜单句柄

HWND hwndParent;

//窗口句柄

int cy;

//窗口的高度

int cx;

//窗口的宽度

int y;

//窗口的左上角的y座标

int x;

//窗口的左上角的x座标

LONG style;

//窗口的类型

LPCSTR lpszName;

//指向窗口的名称的指针

LPCSTR lpszClass;

//指向窗口类的名称的指针

DWORD dwExStyle;

//窗口的扩展类型

} CREATESTRUCTW, *PCREATESTRUCTW, *LPCREATESTRUCTW;

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

if( !CFrameWnd::PreCreateWindow(cs) )

return FALSE;

// TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或样式

= 220;

= 150;

return TRUE;

}

三、框架类、文档类、视图类的关系

框架类主要处理窗口的类型、边框、位置、大小以及子窗口(对于多文档来说)等内容,工具条、状态条也是这个类的成员变量。如果要添加成员变量时,初始化要在CMainFrame()构造函数中添加代码;OnCreate()成员函数是消息WM_CREATE的处理函数,这里面根据需要修改代码(一般不需要);PreCreateWindow()成员函数是创建主窗口前的设置,调整窗口位置、大小 、类型等都是在这儿修改代码。

文档类主要用来处理数据。例如画一个图,图的各节点座标、颜色、线条的粗细等都有文档类来处理,文件的保存和打开等当然也是文档类来处理。视图类主要处理数据的显示,例如:文字在窗口中显示、键盘鼠标操作等都是由视图类来处理。视图类不断地从文档类取数据,不断地对文档类传送数据。当文档类数据较多时,视图类显示的数据只是文档类的一部分。

四、主应用程序类做什么

主应用程序类是程序的入口,其它各个类都是在这儿集成的。这个类除构造函数之外,唯一要注意的就是初始化函数。这个类一般不修改。

单文档

Ctest2App() 构造函数

对话框

Ctest2App() 构造函数

初始化成InitInstance() 初始化成员函数 InitInstance()

员函数

OnAppAbout()

由菜单发出的消息

处理函数

单文档

BOOL Ctest2App::InitInstance()

{

InitCommonControls();

CWinApp::InitInstance();

if (!AfxOleInit())

{

AfxMessageBox(IDP_OLE_INIT_FAILED);

return FALSE;

}

{

对话框

BOOL Ctest3App::InitInstance()

InitCommonControls();

CWinApp::InitInstance();

AfxEnableControlContainer();

SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

//加载标准 INI 文件选项(包括

AfxEnableControlContainer();

SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

//上面内容不用太关注,以下是重MRU)

LoadStdProfileSettings(4);

//上面内容不用太关注,以下是重点 点

//注册应用程序的文档模板。文档模 Ctest3Dlg dlg;

m_pMainWnd = &dlg;

//将用作文档、框架窗口和视图之间 INT_PTR nResponse = l();

的连接

//下面的程序注意:对话框窗口已 CSingleDocTemplate* pDocTemplate;

经被关闭

pDocTemplate =

new CSingleDocTemplate(

if (nResponse == IDOK)

IDR_MAINFRAME,

RUNTIME_CLASS(Ctest2Doc),

RUNTIME_CLASS(CMainFrame),

RUNTIME_CLASS(Ctest2View));

if (!pDocTemplate)

return FALSE;

AddDocTemplate(pDocTemplate);

{

// TODO: 在此放置处理何时用“确定”来关闭

//对话框的代码

}

else if (nResponse == IDCANCEL)

{

//分析标准外壳命令、DDE、打开文件

// TODO: 在此放置处理何时用操作的命令行

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

“取消”来关闭

//对话框的代码

}

//调度在命令行中指定的命令。如果

//用 /RegServer、/Register、

// 由于对话框已关闭,所以将返回

/Unregserver FALSE 以便退出应用程序,

//或 /Unregister 启动应用程序, // 而不是启动应用程序的消息则返回 FALSE。

泵。

if (!ProcessShellCommand(cmdInfo))

return FALSE;

}

return FALSE;

//唯一的一个窗口已初始化,因此显

示它并对其进行更新

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

//仅当存在后缀时才调用

DragAcceptFiles,

//在 SDI 应用程序中,这应在

ProcessShellCommand

//之后发生

return TRUE;

}

04章

程序的执行流程

一、WinMain()函数在哪儿

因为看不到WinMain()函数,许多初学者不知道程序究竟从哪儿开始,编程从哪儿下手,编写的代码何时运行等等,另外,还有很多看不懂的语句,最大的困难恐怕还是MFC的编程思想不太理解。各位既然选择小雅的教程来学习MFC,小雅就有责任将每一个你不明白的地方剖析清楚,因为小雅自己也曾为此而烦恼了几年。不过,饭还得一口一口地吃,最好的办法是先照葫芦画瓢,从容易的入手,再一步一步地深入。

MFC把千篇一律的WinMain()函数写在①crtexec.c中,在编译完后链接时才组装到你的exe程序中。运行时,这个WinMain()函数调用MFC的全局函数AfxWinMain(),这个全局函数AfxWinMain()是写在②中的。在AfxWinMain()函数中做三件事,③注册窗口类、④调用应用程序类的初始化函数InitInstance()、⑤调用应用程序类的Run()函数(实际上是父类的父类CWinThread的Run()成员函数)。

上面这个Run()函数中包含消息循环,而InitInstance()函数又将框架类、文档类、视图类结合在一起,于是所有的类全部集成起来了。编程的重点在文档类和视图类,什么样的消息,执行什么样的处理函数,这样就有效地将数据和控制分离开来。

二、消息处理

消息处理的回调函数在中的CWnd::WindowProc(...),由上面讲的Run()函数调用,然后在主应用程序类、框架类、文档类、视图类里面接受这些消息并处理。问题是:某一消息究竟在哪个类里面处理?

与窗口有关的消息(即以“WM_”开头的消息),只能在框架类和视图类中处理;来自菜单或工具条的命令事件,则框架类、视图类、主应用程序类、文档类全部可以处理,究竟在哪个类中处理,根据具体情况再决定在哪个类中处理。这其中有优先级别高低的问题,一个类接受到命令消息时,先传给比自己级别高的类,如果未处理再自己处理。如果自己也不处理,则传给比自己级别低的类。

这个优先级别是:视图类>文档类>文档模板类>框架类>主应用程序类。例如,框架类收到“编辑”菜单的“Clear All”命令消息时,首先传给视图类,如果视图类处理便结束,否则就传给文档类。如果文档类处理便结束,否则就传给文档模板类。如果文档模板类处理则结束,否则就回到了框架类。如果框架类处理则结束,否则就传给主应用程序类。如果主应用程序类还不处理,就回到MFC缺省的处理程序中。

三、添加、删除消息处理

添加一个消息处理,一般要改动三个地方,以WM_LBUTTONDOWN消息为例,①在视图类的定义(test2View.h)中添加一个成员函数,②在视图类(test2View.c)中添加一条消息映射,③在视图类(test2View.c)中实现消息处理的成员函数。

// test2View.h : Ctest2View 类的接口

......(省略)

// 生成的消息映射函数

protected:

DECLARE_MESSAGE_MAP()

public:

afx_msg

void OnLButtonDown(UINT nFlags, CPoint point);

};

......(省略)

// : Ctest2View 类的实现

//

#include "stdafx.h"

#include "test2.h"

#include "test2Doc.h"

#include "test2View.h"

#include ".test2view.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// Ctest2View

IMPLEMENT_DYNCREATE(Ctest2View, CView)

BEGIN_MESSAGE_MAP(Ctest2View, CView)

// 标准打印命令

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

ON_WM_LBUTTONDOWN()

END_MESSAGE_MAP()

......(省略)

// Ctest2View 消息处理程序

void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

AfxMessageBox("劝学网 /", MB_OK);

CView::OnLButtonDown(nFlags, point);

}

上面虽然很简单,但不建议手动添加,应该在“类视图”中选中“Ctest2View”,再在“属性”工具中选择“WM_LBUTTONDOWN”消息,同时选上“OnLButtonDown”后回车就可以了。

用同样方法在“属性”工具中删除“WM_LBUTTONDOWN”消息时,前面添加的三处代码将被用“//”注释在那儿,成了“垃圾”代码。这时最好手动删除一下这三处“垃圾”代码。

四、程序的入口

学过C++语言或Java语言的人都明白,类相当于自定义的数据类型,如果没有实例,自身是不会运行的。那么,上面一直在解说这4个类,当前生成的5个.cpp文件中,WinMain启动后究竟先运行什么语句,后运行什么语句?

要明白MFC的细节,除了C++的基础之外,最好学一点SDK编程,了解Win32编程中的基本内容。小雅在SDK教程中的第1章到第3章的内容请参考。MFC生成的4个类中,主应用程序类是从CWinApp类继承的,作用相当于WinMain程序;CMainFrame是从CFrameWnd类继承的,其作用相当于WndProc程序。也就是说,消息首先由框架类接受,然后按优先顺序传给其它的类。文档类和视图类由消息来驱动(视图类的PreCreateWindow()除外)。

主应用程序类的程序中,跳过消息映射以及类的构造函数、成员函数后,实际只剩下1条语句,即“Ctest2App theApp;”,这便是整个应用程序的总入口。MFC的crtexec.c的WinMain()函数调用中的“Ctest2App theApp;”。theApp是类的实例,自然也是必然要调用主应用程序类的构造函数,包括父类的构造函数,

之后再调用MFC的中的AfxWinMain()函数,由AfxWinMain()函数调用主应用程序类的InitInstance()成员函数。

在主应用程序类的InitInstance()成员函数中,定义了框架类、文档类、视图类的实例,从而间接地调用了框架类的PreCreateWindow()和OnCreate()成员函数,由OnCreate()函数再调用视图类的PreCreateWindow()成员函数。然后再回到主应用程序类的InitInstance()成员函数中,执行“m_pMainWnd->ShowWindow(SW_SHOW);”和“m_pMainWnd->UpdateWindow();”。

05章 MessageBox和TRACE

一、公共对话框MessageBox

前面章节都是让人脑细胞高度紧张,这一章放松一下。对于公共对话框MessageBox大家并不陌生,在MFC的CWnd类中封装了一个MessageBox()成员函数,但MSDN并不推荐使用,一般用AfxMessageBox()全局函数,任何地方都可使用。

int AfxMessageBox(

int AFXAPI AfxMessageBox(

LPCTSTR lpszText, UINT nIDPrompt,

//要表示的字符串

UINT nType = MB_OK, UINT nType = MB_OK,

//画面形状(按钮、模式、图标、缺省值4个值组合使用)

UINT nIDHelp = 0 UINT nIDHelp = (UINT) -1

//帮助ID,缺省为0

); );

返回值

0

IDYES

IDNO

IDOK

IDCANCEL

IDABORT

IDIGNORE

IDRETRY

不能正常表示

说明

被按下

被按下

被按下

被按下或【ESC】被按下

被按下

被按下

被按下

显示按钮

ID_ABORTRETRYIGNORE

ID_OK

ID_OKCANCEL

ID_RETRYCANCEL

ID_YESNO

ID_YESNOCANCEL

模式

ID_APPMODAL

ID_SYSTEMMODAL

ID_TASKMODAL

显示图标

ID_ICONEXCLAMATION

ID_ICONINFORMATION

ID_ICONquestion

ID_ICONSTOP

缺省按钮

ID_DEFBUTTON1

ID_DEFBUTTON2

ID_DEFBUTTON3

说明

说明

模态(缺省值)即不关闭就不能回到父窗口,但可以切换到其它应用程序。

系统模态即不关闭就不能回到任何窗口。

特殊用途而备用。

说明

感叹符号

情报符号

问号

停止符号

说明

第一个探针为缺省按钮。

第二个探针为缺省按钮。

第三个探针为缺省按钮。

二、MFC特有的规则

类型:MFC将C++的关键字用宏定义成以下内容。

MFC类型

BOOL,BOOLEAN

BYTE

WORD

DWORD

UINT

VOID

LPDWORD

布尔型

意思

1字节数值型

2字节数值型

4字节数值型

无符号整型

void型

DWORD的指针型

char

C++类型

boolean

short

int

unsigned int

void

int*

LPCSTR

LPSTR

LPCVOID

LPVOID

常量字符串

字符串

常量void指针

void指针

const char*

char*

const void*

void*

常量:MFC将C++的常量用宏定义成以下内容。

MFC常量

NULL

TRUE

FALSE

空指针

意思

0

布尔值:真

布尔值:假

C++常量

true

false

命名规则:MFC有以下命名规则。

1. 类名:以大写的C开头,单词的首字母大写,单词之间不用下划线。

2. 成员函数和全局函数:以单词的首字母大写,单词之间不用下划线。全局函数前再加Afx

3. 成员变量:匈牙利命名法,以“m_”开头,单词的首字母大写。

4. 宏、类型名:全部使用大写。

三、用TRACE宏调试

可以在程序的任何位置插入TRACE()宏函数来调试程序,TRACE()宏函数只在DEBUG状态有效,即按运行时有效,时无效。另外,断点也可以设置条件。下面例子是一个没有消息循环的MFC程序(控制台程序)。断点的条件是“i>5”。

// : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include "test5.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

// 初始化 MFC 并在失败时显示错误

if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))

{

// TODO: 更改错误代码以符合您的需要

_tprintf(_T("致命错误: MFC 初始化失败n"));

nRetCode = 1;

}

else

{

// TODO: 在此处为应用程序的行为编写代码。

for (int i=0; i<10; i++) {

TRACE("i = [%d]n", i);

//按钮:[是(Y)][否(N)][取消],图标为:[i],

//模式:不能切换到其它窗口,缺省:[否(N)]

AfxMessageBox("这是小雅的MFC教程。" ,

MB_YESNOCANCEL|MB_SYSTEMMODAL|MB_ICONINFORMATION|MB_DEFBUTTON2);

printf("劝学网:测试AfxMessageBox功能。n");

}

}

return nRetCode;

}

06章

字符串类CString

C语言的字符串即字符数组,在编程时时时要当心是否分配了足够的空间。用MFC中的CString来处理字符串,那些容易出错的麻烦事一概不存在,使用非常方便。

一、WinMain()函数在哪儿

MFC把千篇一律的WinMain()函数写在①crtexec.c中,在编译完后链接时才组装到你的exe程序中。运行时,这个WinMain()函数调用MFC的全局函数AfxWinMain(),这个全局函数AfxWinMain()是写在②中的。在AfxWinMain()函数中做三件事,③注册窗口类、④调用应用程序类的初始化函数InitInstance()、⑤调用应用程序类的Run()函数(实际上是父类的父类CWinThread的Run()成员函数)。

上面这个Run()函数中包含消息循环,而InitInstance()函数又将框架类、文档类、视图类结合在一起,于是所有的类全部集成起来了。编程的重点在文档类和视图类,什么样的消息,执行什么样的处理函数,这样就有效地将数据和控制分离开来。

二、消息处理

消息处理的回调函数在中的CWnd::WindowProc(...),由上面讲的Run()函数调用,然后在主应用程序类、框架类、文档类、视图类里面接受这些消息并处理。问题是:某一消息究竟在哪个类里面处理?

与窗口有关的消息(即以“WM_”开头的消息),只能在框架类和视图类中处理;来自菜单或工具条的命令事件,则框架类、视图类、主应用程序类、文档类全部可以处理,究竟在哪个类中处理,根据具体情况再决定在哪个类中处理。这其中有优先级别高低的问题,一个类接受到命令消息时,先传给比自己级别高的类,如果未处理再自己处理。如果自己也不处理,则传给比自己级别低的类。

这个优先级别是:视图类>文档类>文档模板类>框架类>主应用程序类。例如,框架类收到“编辑”菜单的“Clear All”命令消息时,首先传给视图类,如果视图类处理便结束,否则就传给文档类。如果文档类处理便结束,否则就传给文档模板类。如果文档模板类处理则结束,否则就回到了框架类。如果框架类处理则结束,否则就传给主应用程序类。如果主应用程序类还不处理,就回到MFC缺省的处理程序中。

三、添加、删除消息处理

添加一个消息处理,一般要改动三个地方,以WM_LBUTTONDOWN消息为例,①在视图类的定义(test2View.h)中添加一个成员函数,②在视图类(test2View.c)中添加一条消息映射,③在视图类(test2View.c)中实现消息处理的成员函数。

// test2View.h :

Ctest2View 类的接口

......(省略)

// 生成的消息映射函数

protected:

DECLARE_MESSAGE_MAP()

public:

afx_msg

void OnLButtonDown(UINT nFlags, CPoint point);

};

......(省略)

// : Ctest2View 类的实现

//

#include "stdafx.h"

#include "test2.h"

#include "test2Doc.h"

#include "test2View.h"

#include ".test2view.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// Ctest2View

IMPLEMENT_DYNCREATE(Ctest2View, CView)

BEGIN_MESSAGE_MAP(Ctest2View, CView)

// 标准打印命令

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

ON_WM_LBUTTONDOWN()

END_MESSAGE_MAP()

......(省略)

// Ctest2View 消息处理程序

void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

AfxMessageBox("劝学网 /", MB_OK);

CView::OnLButtonDown(nFlags, point);

}

上面虽然很简单,但不建议手动添加,应该在“类视图”中选中“Ctest2View”,再在“属性”工具中选择“WM_LBUTTONDOWN”消息,同时选上“OnLButtonDown”后回车就可以了。

用同样方法在“属性”工具中删除“WM_LBUTTONDOWN”消息时,前面添加的三处代码将被用“//”注释在那儿,成了“垃圾”代码。这时最好手动删除一下这三处“垃圾”代码。

四、MFC特有的规则

类型:MFC将C++的关键字用宏定义成以下内容。

MFC类型

BOOL,BOOLEAN

BYTE

WORD

DWORD

UINT

VOID

LPDWORD

LPCSTR

LPSTR

LPCVOID

LPVOID

布尔型

意思

1字节数值型

2字节数值型

4字节数值型

无符号整型

void型

DWORD的指针型

常量字符串

字符串

常量void指针

void指针

char

C++类型

boolean

short

int

unsigned int

void

int*

const char*

char*

const void*

void*

常量:MFC将C++的常量用宏定义成以下内容。

MFC常量

NULL 空指针

意思

0

C++常量

TRUE

FALSE

布尔值:真

布尔值:假

true

false

命名规则:MFC有以下命名规则。

1. 类名:以大写的C开头,单词的首字母大写,单词之间不用下划线。

2. 成员函数和全局函数:以单词的首字母大写,单词之间不用下划线。全局函数前再加Afx

3. 成员变量:匈牙利命名法,以“m_”开头,单词的首字母大写。

4. 宏、类型名:全部使用大写。