《CMFC基础教程初学者.ppt》由会员分享,可在线阅读,更多相关《CMFC基础教程初学者.ppt(442页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、ppt简介共14章、442页,涵盖了vc的基础介绍,以及mfc程序的创建,控件的使用、多线程、多媒体编程、网络编程、动态链接库的使用等等,是那些喜欢MFC但是不知道从何学起的MFC爱好者的最佳的基础教材!第1章 Visual C+集成开发环境第2章 MFC与应用程序框架第3章 C+语言基础第4章 C+的面向对象 65第5章 菜单栏、工具栏和状态栏第六章 对话框 108页始 第七章 控件 134页始第八章 MFC常用类 171页始第九章 多媒体类第10章 DLL的开发与调用 237第11章 文档和视图 271第12章 数据库编程 296第13章 多线程编程 340第14章 WinSock网络通信
2、开发 395第1章 Visual C+集成开发环境Visual C+是一个功能强大的可视化应用程序开发工具。其凭借强大功能,受大了广大程序员的欢迎。当今流行的Visual C+的开发工具是6.0版本。下面来介绍Visual C+6.0的一些基本情况。1.1 Visual C+6.0概述Visual C+是一种C/C+语言的集成开发环境(IDE)。当最初还处于DOS时代的Borland公司推出了Turbo Pascal和Turbo C,让程序员感受到了把编译器和编辑器集成在一起使用时的方便。Microsoft公司也看到了这一点,于是,两个公司开始合作,推出了Quick C和Microsoft C
3、/C+等多个DOS版本的C/C+集成开发环境。随着Windows的不断成熟,于是Microsoft开始开发Windows下的Visual C+。经过多次版本的修订与更新,现在大多数程序员使用的是Visual C+6.0版本。Visual C+是一个可视化的C+的集成开发环境。在使用Visual C+时,开发人员可以通过鼠标拖动方便地设计程序的界面,相应的代码系统会自动生成。MFC(Microsoft Fundermental Classes)是微软提供的Visual C+可以调用的类库,其中封装了开发人员常用的类。使用MFC可以大大提高编程人员的工作效率。1.2 Visual C+6.0界面介
4、绍安装好Visual C+开发环境后,桌面上并没有其快捷图标,需读者自己修改。选择“开始”菜单,从所有程序中,找到Microsoft Visual Studio 6.0级联菜单下的Microsoft Visual C+菜单项。选择该菜单项,并将其拖动到桌面上,则在桌面上创建了Microsoft Visual Studio的快捷图标。双击快捷图标,即可启动Visual C+6.0集成开发环境。每次运行Visual C+6.0时,会弹出一个【Tip of the day】对话框,如下图所示。1.2 Visual C+6.0界面介绍技巧:【Tip of the day】对话框中介绍了很多关于开发环境
5、的使用方法以及编程方面的小技巧。如果想在下次启动Visual C+6.0时不再显示该提示对话框,可以选择下一次启动时不再显示它。1.2 Visual C+6.0界面介绍为了便于说明,首先创建一个IDE的MFC项目(具体创建步骤,后面会详细介绍),如下图所示。1.2 Visual C+6.0界面介绍从上图中可以看出,Visual C+的界面被分成了7部分。上面依次标题栏、菜单栏和工具栏。中间左侧部分是工作区窗口,右侧部分是编辑区。最下方是输出窗口和状态栏。1.2.1 工作区窗口和输出窗口工作区窗口和输出窗口是在一个程序编译时使用最多的停靠式窗口。工作区窗口中显示了当前程序中的所有类、资源和文件信
6、息。工作区窗口分为3个部分:Class View(类视图)、Resource View(资源视图)和(文件视图)。Class View:显示当前工作区中所有工程定义的C+类。双击某个类名,Visual C+会自动打开这个类的文件,并将编辑区定位到该类的定义处。双击类中的成员函数和变量,编辑区则定位到该成员函数或变量的定义处。1.2.1 工作区窗口和输出窗口Resource View:显示当前工作区中所有的资源。这些资源包括快捷键、对话框、图标、菜单、字符条编辑器、工具栏和版本信息。双击其中的ID号,会显示相应的资源信息。:显示属于当前工程的所有文件,包括C/C+源文件、头文件、资源文件等。输出
7、窗口与工作区一样,分为多个选择卡。其中最常用的选项卡被放在了最外面,分别为Build、Debug、Find in Files 1和Find in Files 2。1.2.1 工作区窗口和输出窗口Build:Build显示工程在创建中,经过的每一个步骤及相应信息。如果出现编译链接错误,那么出现错误的文件、原因、行号等内容会显示在Build中。双击该错误信息行,编辑区则定位在该错误出现的行。Debug:工程通过编译后,运行调试版本时,Debug选项卡中会显示具体的调试信息。1.2.1 工作区窗口和输出窗口Find in Files 1和Find in Files 2:两个选项卡的作用相同,用于显示
8、从多个文件中查找字符串的结果。当用户想要查看某个函数或变量出现在哪些文件中,单击【Edit】|【Find in Files】命令,弹出【Find in Files】对话框,如下图所示。在【Find in Files】对话框中的【Find what】后的编辑框中,输入想要查找的内容,单击【Find】按钮即可。查找到的内容会输出到Find in Files选项卡中。1.2.2 菜单栏和工具栏菜单栏位于集成开发环境的顶部。菜单栏由9个菜单项组成:File(文件)、Edit(编辑)、View(视图)、Insert(插入)、Project(工程)、Build(编译)、Tools(工具)、Windows(
9、窗口)、Help(帮助)。每一个菜单项都有一个下拉式菜单,其中的菜单项用于完成特定的功能。工具栏位于菜单栏的下面。工具栏可以称作是美化的菜单栏,其由许多按钮构成。其中的按钮用于完成特定的功能。工具栏可以任意拖动,也可以成为一个浮动窗口。1.2.3 编辑区在Visual C+中,编写应用程序代码的位置就是编辑区。该编辑区的功能十分强大,智能化程度也非常高。在编辑区内,除了能编写C/C+语言外,还能编写SQL、HTML和VBScript等其他编程语言。如下图所示。1.2.4 联机帮助Visual C+6.0不像其他集成开发环境一样把帮助系统集成在开发环境内部,而是提供了一个专门为Visual C+
10、设计的MSDN类库。MSDN虽然是一个独立的帮助系统,但却能很好地和Visual C+6.0集成在一起。MSDN的使用方式有以下几种:单击【Help】|【Contents】命令;单击【Help】|【Search】命令;单击【Help】|【Index】命令;按下【F2】键。通过上述几种命令方式,即可运行MSDN。1.2.4 Visual C+中的文件扩展名打开程序Hello World所在的文件夹,看到该文件夹自动生成了许多扩展名不同的文件,如下图所示。1.2.6 Visual C+中的文件扩展名了解这些不同的扩展名文件,对于理解Visual C+6.0如何组织和管理项目文件是很有必要的。有关这
11、些文件扩展名及其说明,如下表所示。第2章 MFC与应用程序框架在Visual C+集成开发环境下,使用微软基础类库MFC,可以开发出功能强大的Windows应用程序。另外,通过MFC AppWizard自动生成的MFC应用程序框架,还可以很方便地开发自己想要实现的功能。本章将先介绍有关MFC的基础知识,然后对MFC应用程序框架作具体介绍。2.1 微软基础类库MFCMFC是一种重要的编程方法,它是微软公司的特定的应用程序包装接口。本节将讲解MFC概述及其类库结构。2.1.1 MFC概述MFC的英文全称是Microsoft Foundation Classes,即微软的基础类库。MFC的本质就是一
12、个包含了许多微软公司已经定义好的对象的类库。虽然开发人员要编写的程序在功能上各有不同,但是从结构上讲,都可以化分为对用户界面的设计、对文件的操作、对数据库的访问及对多媒体的使用等一些最主要的方面。这一点正是微软提出MFC类库最重要的原因。在MFC类库中,大约有200个类。在进行程序设计时,只需简单地调用已有的类及类中的方法即可。另外,还可以利用“继承”方法从已有类中派生出自己想要的类。这时,派生出来的类不但拥有父类中的方法和属性,还可以根据自己的需求,自定义一些特殊的属性和方法,使得派生类功能更加强大。MFC有较好的移植性,可应用于众多平台。2.1.2 MFC类库结构MFC中类可划分为基类、应
13、用程序结构类、窗口类、OLE类、数据库类等10大类,而且在其中的一些大类的基础上又派生出许多子类。MFC的类库结构的层次图如下图所示。2.1.2 MFC类库结构从上图中可以看出,CObject是一个原始基类。绝大多数MFC类的最终基类都是CObject。原始基类下面,主要包括以下几种类:MFC应用程序结构类,窗口、对话框和控件类,输出(设备文本)和绘图类,简单数据类型类,数组、列表和映射类,文件和数据库类,Internet和网络类,OLE类以及高度和异常类。MFC的应用程序结构类分为CWinApp和CWinThread。使用MFC创建的每一个应用程序都包含一个由类CWinApp派生而来的应用程
14、序对象。该对象是一个全局对象。应用程序对象主要用于处理应用程序的初始化,同时也处理应用程序事件的消息循环。CCmdTarget和CCmdUI为MFC中常用的有关发送命令的类。CDocument为MFC中常用的应用程序文档的基类。CDocTemplate为文档模版类,通常是应用程序的单文档或多文档的基类。CView类是常用的视图类。2.2 MFC应用程序框架分析在前面介绍过如何创建一个基于单文档的应用程序。对于如何选择性地创建基于多文档或是基于对话框的应用程序,将会在后续章节详细介绍。本节主要对MFC应用程序框进行简单的概括,使读者了解MFC应用程序框架的结构与工作机制。2.2.1 入口函数入口
15、函数就是指一个程序的入口点。WinMain函数是Windows程序的入口函数。为了便于讲解,首先要创建一个MFC应用程序,程序名命名为sample0201。具体创建步骤不再详细介绍。从创建好的sample0201程序中,并不能找到WinMain函数。这是因为典型Windows程序的大部分初始化工作都是标准化的,因此把WinMain函数隐藏在应用程序的框架中。当一个程序编译时,会自动将该函数链接到程序中。在计算机中找到Visual C+的安装目录,笔者安装在F盘,则按照下面这个路径依次打开文件夹,“F:Program FilesMicrosoft Visual StudioVC98MFCSRC”
16、。打开后,会发现一个源文件“WinMain.cpp”。该文件中则定义了MFC应用程序的入口函数AfxWinMain。文件的源代码如下:2.2.1 入口函数2.2.1 入口函数当一个应用程序启动时,会自动调用应用程序框架内部的AfxWinMain函数。根据其前缀Afx就知道WinMain是一个全局的MFC函数。从上述代码中可以看出,WinMain函数会查找该应用程序的一个全局构造对象。该对象是由CWinApp的派生类创建,因此有程序启动时,它就被创建好了。然后WinMain对该应用程序进行初始化,在此过程调用的是该程序全局构造对象的InitApplication()和InitInstance()
17、函数。完成初始化后,WinMain调用Run()函数,运行应用程序的消息循环。最后结束应用程序时,WinMain调用AfxWinTerm()函数,做一些清理工作。2.2.2 InitInstance()函数InitInstance()函数的作用是初始化程序。每次启动一个应用程序时,Winmain函数会自动调用InitInstance()函数。打开创建的程序sample0201,在该程序的CSample0201App类中,可以看到该程序对InitInstance()函数进行了重载。该重载代码如下:2.2.2 InitInstance()函数从上述代码中可以看出,在ShowWindow和Updat
18、eWindos之前,程序要做两个动作,一个是注册窗口类,另一个是构建窗口类。InitInstance()函数规定了生成的应用程序是基于单文档的、基于多文档的或是基于对话框的。因此在CWinApp中必须重载InitInstance()函数。2.2.3 应用类Run()函数与查找WinMain函数类似,在Visual C+的安装目录下,按照下面这个路径依次打开文件夹,“F:Program FilesMicrosoft Visual StudioVC98MFCSRC”。打开后,会发现一个源文件“THRDCORE.CPP”。该文件中定义了应用类Run()函数,源代码如下:2.2.3 应用类Run()函
19、数从上述代码中可以看出,如果消息队列没有消息,则调用OnIdle()函数,并递增iIdleCount计数标志,该计数标志表示在两次消息处理过程只共调用了多少次OnIdle()函数。bIdle是消息队列空闲的标志,当消息队列有消息时,则调用PumpMessage()函数,进行消息翻译和消息派发。其中PreTranslateMessage(&m_msgCur)对消息进行翻译,DispatchMessage(&m_msgCur)把消息m_madCur发送到应用程序主框架窗口。注意:在应用程序sample0201的CSample0201App的Run函数继承了CWinApp的虚函数Run()。而CWi
20、nApp的Run()函数调用了CWinThread的Run()函数。2.2.4 消息映射表当MFC应用程序类中的Run()函数把消息交给主窗口的窗口函数后,窗口函数将如何处理这些消息。在Win32程序中,处理窗口消息的窗口函数WinProc()函数通过switchcase结构对消息进行判断并分别进行处理。但在MFC应用程序的主窗口类对消息的处理并没有沿袭Win32程序的做法。MFC应用程序中进行消息处理,是为每一个要处理的消息添加一个消息处理函数。这种定义消息和消息处理函数的对照表,称为消息映射表。MFC的消息映射表采用映射宏的方式,将消息和消息处理函数一一对应起来。以应用程序的主框架为例,在
21、类的声明文件MainFrame.h中添加声明消息映射的宏。2.2.4 消息映射表在类的实现文件MainFrame.cpp中,添加消息映射宏的定义语句。其中,ID_MY_MESSAGE为自定义的菜单项命令ID号,OnMymessage()为响应菜单命令的成员函数。2.2.5 MFC消息分类MFC应用程序对消息的描述一般分为3类:窗口消息、命令消息和控件消息。1窗口消息窗口消息一般与创建窗口、绘制窗口、移动窗口和销毁窗口等操作有关。另外,当使用鼠标、键盘等与操作窗口有关的动作时,也会产生窗口消息。窗口消息的一般的表示形式是以“WM_”开头的消息。常见的窗口消息如下所述。WM_CHAR:使用键盘时产
22、生的消息。其对应的消息处理函数为OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)。WM_CREATE:创建窗口时产生的消息,用于窗口的初始化。其对应的消息处理函数为OnCreate(LPCREATESTRUCT lpCreateStruct)。WM_PAINT:通知窗口对自身进行绘制。一般在移动窗口、改变窗口大小、绘制图形时使用。其对应的消息处理函数为OnPaint()。1窗口消息WM_LBUTTONDOWM:使用鼠标左键时产生的消息。通知窗口单击了左键。其对应的消息处理函数为OnLButtonDown(UINT nFlags,CPoint point)。
23、WM_MOUSEMOVE:移动鼠标时产生的消息。其对应的消息处理函数为OnMouseMove(UINT nFlags,CPoint point)。WM_CLOSE:关闭窗口时产生的消息。其对应的消息处理函数为OnClose()。WM_DESTROY:销毁窗口时产生的消息。其对应的消息处理函数为OnDestroy()。2命令消息命令消息一般与处理用户的某个请求或执行用户的某个命令有关。一般通过选择菜单项、单击工具栏按钮和按加速键产生命令消息。在MFC应用程序中,凡是从基类CCmdTarget派生出来的子类,都能处理命令消息。另外,文档类、视图类和应用程序类都能处理命令消息。创建命令消息时,可以使
24、用MFC Class Wizard建立消息映射和消息处理函数之间的关系。例如,应用程序类发出文件打开命令,假设打开文件对应的菜单资源ID为ID_,则其命令消息如下:3控件消息普通的控件都是子窗口,因为其都继承自CWnd。它们通过向其父窗口(一般为对话框)发关消息,响应用户的动作(如移动鼠标,单击等)。控件消息一般是由按钮(BN_)、编辑框(EN_)、下拉列表框(LBN_)和组合框(CBN_)等控件产生的。其消息映射宏是在消息名前加上ON_,例如:第3章 C+语言基础要使用Visual C+进行Windows应用程序的开发,就要掌握面向对象的思想和C+语言。本章先讲述一个简单的C+程序,然后根据
25、这个程序,向读者介绍C+中语言基础。3.2 C+的基本数据类型及数据数据类型是对数据的一种抽象描述。在计算机程序中能操作的数据有很多种,不同的数据所需要的存储空间有所不同。将数据按照类型进行分类,有助于程序员对于存储空间的分配。本节将具体介绍有关C+中的数据及其所属的数据类型。3.2.2 变量变量是一种特殊的标识符,在变量中可以存储数据。变量中存储的数据可以根据程序的需要而改变,因此称为变量。1定义变量在C+中,使用一个变量必须先定义该变量。C+中定义变量的语法代码如下:定义一个变量需要说明两点,一是变量的类型,二是变量的名称。其中,变量的类型是C+中的数据类型。变量名是用户为变量起的名称。3
26、.2.2 变量C+的变量名由字符及数字等组成。变量名必须满足以下几个条件。变量名只能由字母、数字和下划线(_)组成。变量名必须以字母或下划线开头。变量名不能包含空白字符(换行符、空格和制表符称为空白字符)。变量名不能与保留字名相同。变量名区分大小写。3.2.2 变量2变量赋值如果想要使用一个变量,就要为其进行赋值。如果没有对定义的变量赋值,Visual C+会为该变量默认一个值。例如,如果是一个int类型的变量且没有赋值,Visual C+将默认其值为0。C+中为变量赋值的方法有两种:一种是在定义变量的同时赋值,另一种是在定义变量后赋值。在定义变量的同时赋值,代码如下:在定义变量后赋值,代码如
27、下:3.2.3常量常量与变量相反,是一个不随时间和程序变化而变化的值。C+中,常量的命名规则和变量的大体相同。不同的是,常量名称中的字母都为大写。C+中定义符号常量的语法代码如下:例如,在计算圆形面积的时候,经常用到PI。为了避免重复地输入PI的实际取值,而用下面的形式声明PI的取值。这样,在程序中编译时,会将程序中出现的所有字符串PI全部置换成3.14。如果想要修改程序中PI的值,只需在头文件处修改,全部PI的取值都会发生变化。3.2.3常量C+中定义静态常量的语法代码如下:在C+中,同声符号常量一样,在声明静态常量时,也要对其进行初始化,代码如下:注意:在符号常量中,PI没有类型,不占有存
28、储单元,且容易出错。而在Const常量中,PI有数据类型,并且占有存储单元,有地址,因此可以使用指针指向它。3.3 C+的运算符及表达式运算符和表达式是一种程序语言的基础。运算符的作用是操作变量或表达式。C+中的运算符包括赋值运算符、算术运算符、逻辑运算符、关系运算符、位运算符、逗号运算符、条件运算符等。本节将介绍这些运算符及其所组成的表达式。3.3.1 表达式表达式是C+程序中不可缺少的一部分。表达式是由运算符、操作数(变量、常量或函数等)和标点符号,按照一定规则组成的一个有意义的语句。例如:3.3.2 运算符C+中的运算符就是一种符号,该符号可以用于处理数据。平时有数学计算中所使用的“+”
29、、“-”、“”、“”都属于运算符。只是这些运算符在C+中的表现形式可能与日常生活中有所不同。下面将对C+中的运算符作具体介绍。1赋值运算符赋值运算符是用于为变量或常量指定数值的运算体符。其操作符号为“”,示例代码如下:上述表达式的意义是,把b的值赋值给a。其中,b可以是一个单纯的变量,也可以是一个表达式。3.3.2 运算符2算术运算符算术运算符是用于进行数学运算的运算符。例如,加、减、乘、除等就是算术运算符。操作完成后,返回一个数字型的值。算术运算符包括加法运算符(+)、减法运算符(-)、乘法运算符(*)、除法运算符(/)、模运算符()。上述算术运算符都是二元运算符,该运算符两端的数据必须是数
30、字。3.3.2 运算符3逻辑运算符逻辑运算符,即用于处理逻辑值的运算符。逻辑运算符通常用在条件判断语句或循环语句中,如if、while语句等。C+中的逻辑运算符包括逻辑与运算符(&)、逻辑或运算符(|)、逻辑非运算符(!)。由逻辑运算符构成的表达式,称为逻辑表达式。逻辑表达式的返回值为逻辑值(true或false),一般情况下,1代表true,0代表false。逻辑与运算符可以进行与操作,其操作方法为:如果逻辑与运算符前的数为false(或是可以得出false的逻辑表达式),则返回false,否则返回true;当逻辑与运算符前后两个数都为true时,才返回true。逻辑或运算符可以进行或操作,
31、其操作方法为:只要逻辑或运算符前后的数据中有一个为true(或是可以得出true的逻辑表达式),则返回true;当逻辑或运算符前后两个数都为false时,才返回false。逻辑非运算符要求要操作的数据必须是逻辑值,或是能够转换成逻辑值的逻辑表达式。逻辑非运算符可以进行非操作,其操作方法为:如果要操作的数据为true,则返回false;如果要操作的数据为false,则返回true。3.3.2 运算符4关系运算符关系运算符,即用于比较两个数据关系大小的运算符,并根据比较的结果返回一个逻辑值。关系运算符包括大于运算符()、大于等于运算符(=)、小于运算符()、小于等于运算符(”一起使用。使用cin的
32、语法代码如下:例如,想要从键盘输入一些数据,将使用下述代码:如果想要一次性输入多个数据,不是使用逗号作为分隔符,而应该用“”分隔,应该写成:3.4.1 C+的输入输出2输出语句C+的输出语句用cout表示。其中,cout必须和“”一起使用,使用cout的语法代码如下:例如,想要从键盘输出一些数据,将使用下述代码:如果想要一次性的输出多个数据,同样不是使用逗号作为分隔符,而是每项数据之间用“Close();/关闭记录集m_pConnection-Close();/关闭连接第13章 多线程编程Windows是一个多任务的操作系统。多线程运行可以提高系统的运行效率,因此使用比较广泛。本章将对进程与线
33、程进行简单介绍。内容包括:进程与线程、线程的使用、线程的终止与通信等。通过本章的学习,读者可以在程序中调用其他应用程序,可以实现进程间的通信,可以实现线程的同步。下面对多线程技术进行详细介绍。13.1 进程与线程进程(Process)是应用程序的执行实例。每个进行都可以访问进程中的所有资源。一个进程是由一个或多个进行、代码、数据和应用程序在内存中的其他资源组成。当一个应用程序启动,相应的一个进程进行也会启动,这个进行称为父进程。一个应用程序还可以启动其他进程,被启动的其他进程称为子进程。想要查看进程,可以打开Windows的任务管理器。单击【进程】标签,在【进程】选择卡中可以查看当前系统中的各
34、个进程,如右图所示。进程是资源的分配单位,每个进程都拥有自己的地址空间和上下文环境。13.1 进程与线程进程是线程的容器,线程是进程内部的一个执行单元。一个进程可以有一个或是多个线程,但这些线程仅生存于该进程中。也就是说,线程是在它所属的进程地址空间里执行代码,并在进程的地址空间对数据进行操作。线程用于描述进程中的运行路径。当一个进程被启动时,系统就要创建一个主线程。该主线程是应用程序需要的唯一线程,但进程也可以创建其他线程来完善进程的其他操作。13.2 线程的分类在MFC中,线程分为两类:用户界面线程(User-Interface Thread)和工作者线程(Worker Thread)。用
35、户界面线程:该线程通常用来处理用户的输入并响应用户生成的事件和消息。用户界面线程是从CWinThread类派生而来的。在MFC中,CWinApp对象就是一个用户界面线程。通常情况下,用户界面线程都是主线程。当应用程序启动时,用户界面线程随之启动。当应用程序退出时,用户界面线程也会随之终止。工作者线程:该线程通常不需要用户进行输入,由后台进行处理。因此,工作者线程又被称为后台线程。工作者线程和用户界面线程的区别在于,工作者线程不用从CWinThread类派生。13.3 线程类(CWinThread)在MFC上,CWinThread类封装了对线程的操作。下面对CWinThtead类中的成员函数和成
36、员变量作具体介绍。其中,CWinThread类中常用的函数如下:(1)调用CreateThread()函数可以创建一个新的线程,该函数的原型如下:dwCreateFlags:表示线程创建的标志。nStackSize:表示线程堆栈的大小。lpSecurityAttrs:表示线程的安全属性。13.3 线程类(CWinThread)(2)调用SetThreadPriority()函数可以设置线程的优先级,该函数的原型如下:其中,nPriority参数定义了线程的优先级。其取值如下表所示。13.3 线程类(CWinThread)(3)调用GetThreadPriority()函数可以获取线程的优先级,
37、该函数原型如下:(4)调用PostThreadMessage()函数可以向另一个CWinThread对象发送消息,该函数的原型如下:message:表示用户定义消息的标识。wParam:表示消息的第一个参数。lParam:表示消息的第二个参数。13.3 线程类(CWinThread)(5)调用SuspendThread()函数可以将线程的挂起计数加1,当线程的挂起计数大于0时,该线程将暂停执行,称之为挂起状态。该函数的原型如下:(6)调用ResumeThread()函数可以将线程的挂起计数减1。当线程的挂起计数减少到0时,恢复线程的执行。该函数的原型如下:13.3 线程类(CWinThread
38、)在CWinThread类中,还需要重载一些函数来完成对线程的操作。(1)重载InitInstance()函数用于控制用户界面线程实例的初始化工作,该函数的原型如下:(2)重载ExitInstance()函数用于控制清理操作,该函数的原型如下:(3)重载OnIdle()函数用于控制线程空闲处理操作,该函数的原型如下:lCount:表示计数器。此外,CWinThread类中有以下几个重要的成员变量。m_hThread:表示当前线程的句柄。m_nThreadID:表示当前线程的ID。m_pMainWnd:表示指向应用程序主窗口的指针。13.4 线程的使用在MFC中,创建一个新的线程时,可以使用全局
39、函数AfxBeginThread()。AfxBeginThread()有两种原型,分别用于创建用户界面线程和工作者线程。13.4.1 启用用户界面线程当启用用户界面线程时,调用AfxBeginThread()的原型如下:pThreadClass:表示用户界面线程对象运程时类结构指针。pParam:表示传递给控制函数的参数。nPriority:表示线程的优先级。nStackSize:表示线程堆栈的大小。dwCreateFlags:表示线程创建的标志。lpSecurityAttrs:表示安全属性(SECURITY_ATTRIBUTES)结构指针。13.4.1 启用用户界面线程另外,启用用户界面线程
40、还可以先创建一个CWinThread类的对象,然后调用CreateThread()函数。代码如下:13.4.2 启用工作者线程当启用工作者线程时,调用AfxBeginThread()的原型如下:pfnThreadProc:表示工作者线程的控制函数指针。pParam:表示传递给控制函数的参数。nPriority:表示线程的优先级。nStackSize:表示线程堆栈的大小。dwCreateFlags:表示线程创建的标志。lpSecurityAttrs:表示安全属性(SECURITY_ATTRIBUTES)结构指针。注意:函数的返回值是一个线程指针。一般情况下,需要保存该指针,以便于以后根据该指针来
41、终止该线程。13.4.3 用户界面线程在实际应用过程中,有时需要在线程中创建对话框。此时,可以使用用户界面线程来实现。在MFC中,创建用户界面线程的具体步骤如下:(1)从CWinThread类派生一个子类。(2)在子类的InitInstance()函数中设置线程的主窗口。(3)调用AfxBeginThread()函数创建用户界面线程。13.4.3 用户界面线程【示例13-1】创建用户界面线程。在一个基于对话框的MFC应用程序中,创建一个用户界面线程用来启动另外一个对话框。(1)创建一个基于对话框的MFC应用程序sample1301。(2)设计对话框资源,如下图所示。13.4.3 用户界面线程(
42、3)单击【Insert】|【Resource】命令,弹出【Insert Resource】(插入资源)对话框。选择Dialog结点,如左图所示。(4)设计新插入的对话框资源,如右图所示。13.4.3 用户界面线程(5)双击新添加的对话框资源,为其添加相应的类CSubDlg,如左图所示。(6)单击【Insert】|【New Class】命令,弹出【New Class】对话框。添加一个CWinThread类的子类CSubThread,如右图所示。13.4.3 用户界面线程(7)添加并修改CSubThread类中的数据成员和成员函数。其中,SubThread.h文件中的代码如下:SubThread.
43、cpp文件中的代码如下:13.4.3 用户界面线程(8)在对话框类CSample1301Dlg的【创建用户界面线程】按钮中添加如下代码:在【取消】按钮中添加如下代码:13.4.3 用户界面线程(9)运行程序sample1301,弹出主对话框。单击【创建用户界面线程】按钮,弹出【用户界面线程】对话框。当运行程序sample1301的同时,可以打开系统的任务管理器进行查看。当主对话框弹出时,进程中多了一个sample1301.exe进程。当弹出【用户界面线程】对话框时,没有相应的进程出现。从上述内容中可以看出,本例成功地创建了一个用户界面线程。13.4.4 工作者线程创建工作者线程不需要使用CWi
44、nThread类的派生类,而是需要实现控制函数。在工作者线程的控制函数中,定义了线程的生命周期。当进入控制函数时,线程将被启动。而当退出函数时,线程将被终止。【示例13-2】创建工作者线程。在一个基于对话框的MFC应用程序中,创建工作者线程,并在对话框进行初始化时启用。(1)创建一个基于对话框的MFC应用程序sample1302。(2)设计对话框资源,如下图所示。13.4.4 工作者线程(3)添加成员变量。为两个ListBox控件添加成员变量,分别为m_list1和m_list2。其中,类别为Control,类型为CListBox。(4)为CSample1302Dlg类添加两个自定义函数,分别
45、如下:(5)为控制函数添加代码。其中,FirstThread()函数中添加的代码如下:SecondThread()函数中添加的代码如下:13.4.4 工作者线程(6)在对话框类CSample1302Dlg的OnInitDialog()函数中添加如下代码:(7)修改Sample1302Dlg.h文件中的函数声明,将线程函数声明为static函数。13.4.4 工作者线程(8)运行程序sample1302,结果如图13.8所示。注意:如果不按照第(7)步骤对Sample1302Dlg.h文件进行修改,会出现如下错误提示:AfxBeginThread:none of the 2 overloads
46、can convert parameter 1 from type unsigned int(void*)。13.5 线程的终止当一个线程终止时,关闭该线程所属的所有对象句柄,将该线程对象状态变为有信息号状态,并将该线程对象终止状态STILL_ACTIVE改为退出码。通常情况下,线程终止分为两种:正常终止和异常终止。13.5.1 正常终止线程对于用户界面线程而言,使其正常终止只需在用户界面线程中调用PostQuitMessage()即可。该函数的原型如下:nExitCode:表示线程的退出码。对于工作者线程而言,当控制函数正常执行到函数的结束点(return语句)后,该线程也就正常终止了。另外
47、,用户也可以选择使用AfxEndThread()函数来终止该线程。AfxEndThread()函数的原型如下:13.5.2 异常终止线程异常终止是指线程内部出现自身无法终止的情况,在其他线程中强行终止该线程。异常终止用于在紧急情况下安全退出控制。如果想要终止线程,可以使用TerminateThread()函数。该函数的原型如下:hThread:表示将要终止的线程句柄。该参数可以使用创建线程返回时返回的CWinThread从m_hThread成员变量中得到。dwExitCode:表示线程的退出码。13.5.3 线程的退出码根据线程的退出码,可以判断线程的执行情况。使用GetExitCode()函
48、数可以获取工作者线程或用户界面线程退出码。该函数的原型如下:hThread:表示线程句柄。该参数可以使用创建线程返回时返回的CWinThread从m_hThread成员变量中得到。lpExitCode:表示接收返回的终止状态的地址。13.5.3 线程的退出码如果线程没有被终止,则终止状态返回STILL_ACTIVE。如果线程已经被终止,则返回的终止状态可以是以下取值中的一个:在ExitThread()或TerminateThread()函数中指定的退出值。在线程函数的return语句中的返回值。线程所属进程中的退出值。13.5.3 线程的退出码想要获取线程的退出码,还要做一些其他工作。一般线程
49、终止后,线程对象将被删除,用户不能获取m_hThread句柄。想要解决这个问题,可以采用以下两个办法。(1)在创建线程后,设置线程对象的m_bAutoDelete成员变量为FALSE。这样,CWinThread对象在线程终止后仍将存在。在退出应用程序之前,还需要手动添加删除CWinThread线程对象的代码。(2)单独选择保存线程句柄。在创建线程后,使用Win32函数DuplicateHandle()将m_hThread复制到另一个句柄。这样,尽管对象被自动删除了,但使用复制的句仍然可以获取线程的退出码。注意:采用第二种方法时,在复制句柄之前,线程不能终止。最安全的方法是在创建线程进使用CRE
50、ATE_SUSPENDED挂起,复制好后,调用ResumeThread()函数恢复线程的执行。13.6 线程的通信通常情况下,在次线程是为主线程服务的,这就预示着主线程和次线程之间会进行及时的通信。下面对线程间的通信作简单介绍。13.6.1 通信原理在线程间进行通信的过程中,需要用到以下几个函数。其中,调用PostMessage()函数可以进行线程间的通信。该函数的原型如下:hWnd:表示消息发送的目的窗口句柄。Msg:表示将要发送的消息。wParam:表示消息的第1个参数。lParam:表示消息的第2个参数。13.6.1 通信原理调用PostThreadMessage()函数可以向某个线程发