C++MFC基础教程(初学者)-课件

PPT
  • 阅读 59 次
  • 下载 0 次
  • 页数 442 页
  • 大小 3.196 MB
  • 2022-11-24 上传
  • 收藏
  • 违规举报
  • © 版权认领
下载文档50.00 元 加入VIP免费下载
此文档由【小橙橙】提供上传,收益归文档提供者,本网站只提供存储服务。若此文档侵犯了您的版权,欢迎进行违规举报版权认领
C++MFC基础教程(初学者)-课件
可在后台配置第一页与第二页中间广告代码
C++MFC基础教程(初学者)-课件
可在后台配置第二页与第三页中间广告代码
C++MFC基础教程(初学者)-课件
可在后台配置第三页与第四页中间广告代码
C++MFC基础教程(初学者)-课件
C++MFC基础教程(初学者)-课件
还剩10页未读,继续阅读
【这是免费文档,您可以免费阅读】
/ 442
  • 收藏
  • 违规举报
  • © 版权认领
下载文档50.00 元 加入VIP免费下载
文本内容

【文档说明】C++MFC基础教程(初学者)-课件.ppt,共(442)页,3.196 MB,由小橙橙上传

转载请保留链接:https://www.ichengzhen.cn/view-44703.html

以下为本文档部分文字说明:

ppt简介•共14章、442页,涵盖了vc的基础介绍,以及mfc程序的创建,控件的使用、多线程、多媒体编程、网络编程、动态链接库的使用等等,是那些喜欢MFC但是不知道从何学起的MFC爱好者的最佳的基础教材!第1章V

isualC++集成开发环境•VisualC++是一个功能强大的可视化应用程序开发工具。其凭借强大功能,受大了广大程序员的欢迎。当今流行的VisualC++的开发工具是6.0版本。下面来介绍Visual

C++6.0的一些基本情况。1.1VisualC++6.0概述•VisualC++是一种C/C++语言的集成开发环境(IDE)。当最初还处于DOS时代的Borland公司推出了TurboPascal和TurboC,让程序员感受到了把编译器和编辑器集

成在一起使用时的方便。Microsoft公司也看到了这一点,于是,两个公司开始合作,推出了QuickC和MicrosoftC/C++等多个DOS版本的C/C++集成开发环境。•随着Windows的不断成熟,于是Microso

ft开始开发Windows下的VisualC++。经过多次版本的修订与更新,现在大多数程序员使用的是VisualC++6.0版本。•VisualC++是一个可视化的C++的集成开发环境。在使用VisualC++时,开发人员可以通过鼠标拖动方便地设计程序的界面,相应的代码系统

会自动生成。MFC(MicrosoftFundermentalClasses)是微软提供的VisualC++可以调用的类库,其中封装了开发人员常用的类。使用MFC可以大大提高编程人员的工作效率。1.2VisualC++6.0界面介绍•安装好VisualC++开

发环境后,桌面上并没有其快捷图标,需读者自己修改。选择“开始”菜单,从所有程序中,找到MicrosoftVisualStudio6.0级联菜单下的MicrosoftVisualC++菜单项。选择该菜单项,

并将其拖动到桌面上,则在桌面上创建了MicrosoftVisualStudio的快捷图标。•双击快捷图标,即可启动VisualC++6.0集成开发环境。每次运行VisualC++6.0时,会弹出一个【Tipoftheday】对话框,如下图所示。1.2VisualC++6.0界

面介绍•技巧:【Tipoftheday】对话框中介绍了很多关于开发环境的使用方法以及编程方面的小技巧。如果想在下次启动VisualC++6.0时不再显示该提示对话框,可以选择下一次启动时不再显示它。1.2

VisualC++6.0界面介绍•为了便于说明,首先创建一个IDE的MFC项目(具体创建步骤,后面会详细介绍),如下图所示。1.2VisualC++6.0界面介绍•从上图中可以看出,VisualC++的界面被分成了7部分。上面依次标题栏、菜单栏和工具栏。中间左侧部

分是工作区窗口,右侧部分是编辑区。最下方是输出窗口和状态栏。1.2.1工作区窗口和输出窗口•工作区窗口和输出窗口是在一个程序编译时使用最多的停靠式窗口。工作区窗口中显示了当前程序中的所有类、资源和文件信息。工作区窗口

分为3个部分:ClassView(类视图)、ResourceView(资源视图)和FileView(文件视图)。•ClassView:显示当前工作区中所有工程定义的C++类。双击某个类名,VisualC++会自动打开这个类的文件,并将编辑区定位到该类的定义处。双击类中的成员函

数和变量,编辑区则定位到该成员函数或变量的定义处。1.2.1工作区窗口和输出窗口•ResourceView:显示当前工作区中所有的资源。这些资源包括快捷键、对话框、图标、菜单、字符条编辑器、工具栏和版本信息。双击

其中的ID号,会显示相应的资源信息。•FileView:显示属于当前工程的所有文件,包括C/C++源文件、头文件、资源文件等。•输出窗口与工作区一样,分为多个选择卡。其中最常用的选项卡被放在了最外面,分别为Build、Debug、Fi

ndinFiles1和FindinFiles2。1.2.1工作区窗口和输出窗口•Build:Build显示工程在创建中,经过的每一个步骤及相应信息。如果出现编译链接错误,那么出现错误的文件、原因、行号等内容会显示在Bui

ld中。双击该错误信息行,编辑区则定位在该错误出现的行。•Debug:工程通过编译后,运行调试版本时,Debug选项卡中会显示具体的调试信息。1.2.1工作区窗口和输出窗口•FindinFiles1和FindinFiles2:两个选项卡的作用相同,用于显示从多个文

件中查找字符串的结果。当用户想要查看某个函数或变量出现在哪些文件中,单击【Edit】|【FindinFiles】命令,弹出【FindinFiles】对话框,如下图所示。在【FindinFiles】对话框中的【Findwh

at】后的编辑框中,输入想要查找的内容,单击【Find】按钮即可。查找到的内容会输出到FindinFiles选项卡中。1.2.2菜单栏和工具栏•菜单栏位于集成开发环境的顶部。菜单栏由9个菜单项组成:File(文件)、Ed

it(编辑)、View(视图)、Insert(插入)、Project(工程)、Build(编译)、Tools(工具)、Windows(窗口)、Help(帮助)。每一个菜单项都有一个下拉式菜单,其中的菜单项

用于完成特定的功能。•工具栏位于菜单栏的下面。工具栏可以称作是美化的菜单栏,其由许多按钮构成。其中的按钮用于完成特定的功能。工具栏可以任意拖动,也可以成为一个浮动窗口。1.2.3编辑区•在VisualC++中,编写应用程序代码的位臵就是编辑区。该编辑区的功能十分强大,智能化程度也非常高

。在编辑区内,除了能编写C/C++语言外,还能编写SQL、HTML和VBScript等其他编程语言。如下图所示。1.2.4联机帮助•VisualC++6.0不像其他集成开发环境一样把帮助系统集成在开发环境内部,而是提供了一个专门为Visua

lC++设计的MSDN类库。MSDN虽然是一个独立的帮助系统,但却能很好地和VisualC++6.0集成在一起。•MSDN的使用方式有以下几种:•单击【Help】|【Contents】命令;•单击【Help】|【Sea

rch】命令;•单击【Help】|【Index】命令;•按下【F2】键。•通过上述几种命令方式,即可运行MSDN。1.2.4VisualC++中的文件扩展名•打开程序HelloWorld所在的文件夹,看到该文件夹自动生成了许多扩展名不同的文件,如下图所示。1.2.6VisualC++中的文件扩展名

•了解这些不同的扩展名文件,对于理解VisualC++6.0如何组织和管理项目文件是很有必要的。有关这些文件扩展名及其说明,如下表所示。第2章MFC与应用程序框架•在VisualC++集成开发环境下,使用微软基础类库MFC,

可以开发出功能强大的Windows应用程序。另外,通过MFCAppWizard自动生成的MFC应用程序框架,还可以很方便地开发自己想要实现的功能。本章将先介绍有关MFC的基础知识,然后对MFC应用程序框架作具体介绍。2.1微软基础类库MFC•MFC是一种重要的编程方法,它是微软公司的特

定的应用程序包装接口。本节将讲解MFC概述及其类库结构。2.1.1MFC概述•MFC的英文全称是MicrosoftFoundationClasses,即微软的基础类库。MFC的本质就是一个包含了许多微软公司已经定义好的对象的类库。•虽然开发人员要编写的程序在功能上各

有不同,但是从结构上讲,都可以化分为对用户界面的设计、对文件的操作、对数据库的访问及对多媒体的使用等一些最主要的方面。这一点正是微软提出MFC类库最重要的原因。•在MFC类库中,大约有200个类。在进行程序设计时,只需简单地调用已有的类及类中的

方法即可。另外,还可以利用“继承”方法从已有类中派生出自己想要的类。这时,派生出来的类不但拥有父类中的方法和属性,还可以根据自己的需求,自定义一些特殊的属性和方法,使得派生类功能更加强大。MFC有较好的移植性,可应用于众多平台

。2.1.2MFC类库结构•MFC中类可划分为基类、应用程序结构类、窗口类、OLE类、数据库类等10大类,而且在其中的一些大类的基础上又派生出许多子类。MFC的类库结构的层次图如下图所示。2.1.2MFC类库结构•从上图中可以看出,CObject是一个原始基类。绝大

多数MFC类的最终基类都是CObject。原始基类下面,主要包括以下几种类:MFC应用程序结构类,窗口、对话框和控件类,输出(设备文本)和绘图类,简单数据类型类,数组、列表和映射类,文件和数据库类,Intern

et和网络类,OLE类以及高度和异常类。•MFC的应用程序结构类分为CWinApp和CWinThread。使用MFC创建的每一个应用程序都包含一个由类CWinApp派生而来的应用程序对象。该对象是一个

全局对象。应用程序对象主要用于处理应用程序的初始化,同时也处理应用程序事件的消息循环。•CCmdTarget和CCmdUI为MFC中常用的有关发送命令的类。CDocument为MFC中常用的应用程序文档的基类。CDocTemplate为文档模版类,

通常是应用程序的单文档或多文档的基类。CView类是常用的视图类。2.2MFC应用程序框架分析•在前面介绍过如何创建一个基于单文档的应用程序。对于如何选择性地创建基于多文档或是基于对话框的应用程序,将

会在后续章节详细介绍。本节主要对MFC应用程序框进行简单的概括,使读者了解MFC应用程序框架的结构与工作机制。2.2.1入口函数•入口函数就是指一个程序的入口点。WinMain函数是Windows程序的入口函数。为了便于讲解,首先要创建一个MFC应用程序,程序名命名为sample0201。具体

创建步骤不再详细介绍。•从创建好的sample0201程序中,并不能找到WinMain函数。这是因为典型Windows程序的大部分初始化工作都是标准化的,因此把WinMain函数隐藏在应用程序的框架中。当一个程序编译

时,会自动将该函数链接到程序中。•在计算机中找到VisualC++的安装目录,笔者安装在F盘,则按照下面这个路径依次打开文件夹,“F:\ProgramFiles\MicrosoftVisualStudio\VC98\MFC\SRC‖。打开后,会发现一

个源文件“WinMain.cpp‖。该文件中则定义了MFC应用程序的入口函数AfxWinMain。文件的源代码如下:2.2.1入口函数•……•……2.2.1入口函数•当一个应用程序启动时,会自动调用应用程序框架内部的AfxWinMa

in函数。根据其前缀Afx就知道WinMain是一个全局的MFC函数。从上述代码中可以看出,WinMain函数会查找该应用程序的一个全局构造对象。该对象是由CWinApp的派生类创建,因此有程序启动时,它

就被创建好了。然后WinMain对该应用程序进行初始化,在此过程调用的是该程序全局构造对象的InitApplication()和InitInstance()函数。完成初始化后,WinMain调用Run()函数,运行应用程序的消

息循环。最后结束应用程序时,WinMain调用AfxWinTerm()函数,做一些清理工作。2.2.2InitInstance()函数•InitInstance()函数的作用是初始化程序。每次启动一个应用程序时,Win

main函数会自动调用InitInstance()函数。打开创建的程序sample0201,在该程序的CSample0201App类中,可以看到该程序对InitInstance()函数进行了重载。该重载代码如下:•……•……2.2.2InitInstance()函数•从上述代码中可以

看出,在ShowWindow和UpdateWindos之前,程序要做两个动作,一个是注册窗口类,另一个是构建窗口类。InitInstance()函数规定了生成的应用程序是基于单文档的、基于多文档的或是基于对话框的。因此在CWinAp

p中必须重载InitInstance()函数。2.2.3应用类Run()函数•与查找WinMain函数类似,在VisualC++的安装目录下,按照下面这个路径依次打开文件夹,“F:\ProgramFiles\Mi

crosoftVisualStudio\VC98\MFC\SRC‖。打开后,会发现一个源文件“THRDCORE.CPP‖。该文件中定义了应用类Run()函数,源代码如下:•……•……2.2.3应用类Run()

函数•从上述代码中可以看出,如果消息队列没有消息,则调用OnIdle()函数,并递增iIdleCount计数标志,该计数标志表示在两次消息处理过程只共调用了多少次OnIdle()函数。bIdle是消息队列空闲的标志,当消息队列有消息时,则调用PumpMe

ssage()函数,进行消息翻译和消息派发。其中PreTranslateMessage(&m_msgCur)对消息进行翻译,DispatchMessage(&m_msgCur)把消息m_madCur发送到应用程序主框架窗口。•注意:在应用程序sampl

e0201的CSample0201App的Run函数继承了CWinApp的虚函数Run()。而CWinApp的Run()函数调用了CWinThread的Run()函数。2.2.4消息映射表•当MFC应用程序类中的Run()函数

把消息交给主窗口的窗口函数后,窗口函数将如何处理这些消息。在Win32程序中,处理窗口消息的窗口函数WinProc()函数通过switch~case结构对消息进行判断并分别进行处理。但在MFC应用程序的主窗口类对消息的处理并没有沿袭Win32程

序的做法。•MFC应用程序中进行消息处理,是为每一个要处理的消息添加一个消息处理函数。这种定义消息和消息处理函数的对照表,称为消息映射表。MFC的消息映射表采用映射宏的方式,将消息和消息处理函数一一对应起来。以

应用程序的主框架为例,在类的声明文件MainFrame.h中添加声明消息映射的宏。2.2.4消息映射表•在类的实现文件MainFrame.cpp中,添加消息映射宏的定义语句。•其中,ID_MY_MESSAGE为自定义的

菜单项命令ID号,OnMymessage()为响应菜单命令的成员函数。2.2.5MFC消息分类•MFC应用程序对消息的描述一般分为3类:窗口消息、命令消息和控件消息。1.窗口消息•窗口消息一般与创建窗口、绘制窗口、移动窗口和销

毁窗口等操作有关。另外,当使用鼠标、键盘等与操作窗口有关的动作时,也会产生窗口消息。窗口消息的一般的表示形式是以“WM_‖开头的消息。常见的窗口消息如下所述。•WM_CHAR:使用键盘时产生的消息。其对应的消

息处理函数为OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)。•WM_CREATE:创建窗口时产生的消息,用于窗口的初始化。其对应的消息处理函数为OnCreate(LPCREATESTRUCTlpCreateStruct)。•WM_

PAINT:通知窗口对自身进行绘制。一般在移动窗口、改变窗口大小、绘制图形时使用。其对应的消息处理函数为OnPaint()。1.窗口消息•WM_LBUTTONDOWM:使用鼠标左键时产生的消息。通知窗口单击了左键。其对应的消息处

理函数为OnLButtonDown(UINTnFlags,CPointpoint)。•WM_MOUSEMOVE:移动鼠标时产生的消息。其对应的消息处理函数为OnMouseMove(UINTnFlags,CPointpoint)。•WM_CLO

SE:关闭窗口时产生的消息。其对应的消息处理函数为OnClose()。•WM_DESTROY:销毁窗口时产生的消息。其对应的消息处理函数为OnDestroy()。2.命令消息•命令消息一般与处理用户的某个请求或执行用户的某个命令有关。一般通过选择菜单项、单击工具栏按钮

和按加速键产生命令消息。在MFC应用程序中,凡是从基类CCmdTarget派生出来的子类,都能处理命令消息。另外,文档类、视图类和应用程序类都能处理命令消息。•创建命令消息时,可以使用MFCClassWizard建立消息映射和消息处理函数之间的关系。例如,应用程序类

发出文件打开命令,假设打开文件对应的菜单资源ID为ID_FILE_IPEN,则其命令消息如下:3.控件消息•普通的控件都是子窗口,因为其都继承自CWnd。它们通过向其父窗口(一般为对话框)发关消息,响应用户的动作(如移动鼠标,单击等)。控件消息一般是由按钮(BN_)、编辑框(EN_

)、下拉列表框(LBN_)和组合框(CBN_)等控件产生的。其消息映射宏是在消息名前加上ON_,例如:第3章C++语言基础•要使用VisualC++进行Windows应用程序的开发,就要掌握面向对象的思想和C++语言。本章先讲述一个简

单的C++程序,然后根据这个程序,向读者介绍C++中语言基础。3.2C++的基本数据类型及数据•数据类型是对数据的一种抽象描述。在计算机程序中能操作的数据有很多种,不同的数据所需要的存储空间有所不同。将数据按照类型进行分类,有助于程序员对于存储空间的分配。本节将具体介

绍有关C++中的数据及其所属的数据类型。3.2.2变量•变量是一种特殊的标识符,在变量中可以存储数据。变量中存储的数据可以根据程序的需要而改变,因此称为变量。•1.定义变量•在C++中,使用一个变量必须先定义该变量。C++中定义变量的语法代码如下:•定义一个变

量需要说明两点,一是变量的类型,二是变量的名称。其中,变量的类型是C++中的数据类型。变量名是用户为变量起的名称。3.2.2变量•C++的变量名由字符及数字等组成。变量名必须满足以下几个条件。•变量名只能由字母、数字和下划线(_)组成。•变量名必须以字母或下划线开头。•变量名不能包含空白

字符(换行符、空格和制表符称为空白字符)。•变量名不能与保留字名相同。•变量名区分大小写。3.2.2变量•2.变量赋值•如果想要使用一个变量,就要为其进行赋值。如果没有对定义的变量赋值,VisualC++

会为该变量默认一个值。例如,如果是一个int类型的变量且没有赋值,VisualC++将默认其值为0。•C++中为变量赋值的方法有两种:一种是在定义变量的同时赋值,另一种是在定义变量后赋值。•在定义变量的同时赋值,代码如下:•在定义变量后赋值,代码如下:3.2.3.常量•常量与变量相反,是一个不随

时间和程序变化而变化的值。C++中,常量的命名规则和变量的大体相同。不同的是,常量名称中的字母都为大写。•C++中定义符号常量的语法代码如下:•例如,在计算圆形面积的时候,经常用到PI。为了避免重复地输入PI的实际取值,而用下面的

形式声明PI的取值。•这样,在程序中编译时,会将程序中出现的所有字符串PI全部臵换成3.14。如果想要修改程序中PI的值,只需在头文件处修改,全部PI的取值都会发生变化。3.2.3.常量•C++中定义静态常量的语法代码如下:•在C++中,同

声符号常量一样,在声明静态常量时,也要对其进行初始化,代码如下:•注意:在符号常量中,PI没有类型,不占有存储单元,且容易出错。而在Const常量中,PI有数据类型,并且占有存储单元,有地址,因此可以使用指针指向它。3.3C++的运算符及表达式•运算符和表达式是一种

程序语言的基础。运算符的作用是操作变量或表达式。C++中的运算符包括赋值运算符、算术运算符、逻辑运算符、关系运算符、位运算符、逗号运算符、条件运算符等。本节将介绍这些运算符及其所组成的表达式。3.3.1表达式•表达式是C++程

序中不可缺少的一部分。表达式是由运算符、操作数(变量、常量或函数等)和标点符号,按照一定规则组成的一个有意义的语句。例如:3.3.2运算符C++中的运算符就是一种符号,该符号可以用于处理数据。平时有数学计算中所使用的“+”、

“-”、“×”、“÷”都属于运算符。只是这些运算符在C++中的表现形式可能与日常生活中有所不同。下面将对C++中的运算符作具体介绍。1.赋值运算符赋值运算符是用于为变量或常量指定数值的运算体符。其操作符号为“=”,示例代码如下:上述表达式的意义是,

把b的值赋值给a。其中,b可以是一个单纯的变量,也可以是一个表达式。3.3.2运算符•2.算术运算符•算术运算符是用于进行数学运算的运算符。例如,加、减、乘、除等就是算术运算符。操作完成后,返回一个数字型的值。算术运算符包括加法运算符(+

)、减法运算符(-)、乘法运算符(*)、除法运算符(/)、模运算符(%)。•上述算术运算符都是二元运算符,该运算符两端的数据必须是数字。3.3.2运算符•3.逻辑运算符•逻辑运算符,即用于处理逻辑值的运算符。逻辑运算符

通常用在条件判断语句或循环语句中,如if、while语句等。C++中的逻辑运算符包括逻辑与运算符(&&)、逻辑或运算符(||)、逻辑非运算符(!)。由逻辑运算符构成的表达式,称为逻辑表达式。逻辑表达式的返回值为

逻辑值(true或false),一般情况下,1代表true,0代表false。•逻辑与运算符可以进行与操作,其操作方法为:如果逻辑与运算符前的数为false(或是可以得出false的逻辑表达式),则返回false,否则返回true;当逻辑与运算符前后两个数都为true时,才返回true。•逻辑或运

算符可以进行或操作,其操作方法为:只要逻辑或运算符前后的数据中有一个为true(或是可以得出true的逻辑表达式),则返回true;当逻辑或运算符前后两个数都为false时,才返回false。•逻辑非运算符要

求要操作的数据必须是逻辑值,或是能够转换成逻辑值的逻辑表达式。逻辑非运算符可以进行非操作,其操作方法为:如果要操作的数据为true,则返回false;如果要操作的数据为false,则返回true。3.3.2运算符•4.关系运算符•关系运算符,

即用于比较两个数据关系大小的运算符,并根据比较的结果返回一个逻辑值。关系运算符包括大于运算符(>)、大于等于运算符(>=)、小于运算符(<)、小于等于运算符(<=)、等于运算符(==)以及不等于运算符(!=)。•5.条件运算符•条件运算符,即用于条件判断的运算符。其构成的表达式格式为:•其中,如

果表达式1的值为非0,则执行表达式2;如果表达式1的值为0,则执行表达式3。3.3.2运算符•6.特殊运算符•C++中还提供了一些特殊的运算符,如++、--、+=、-=等。3.4C++的语句控制•C++中的控制语句主要用于完成分支结构程序和循环结构程序的

控制。其主要包括以下9个控制语句:if~else语句、switch语句、for语句、while语句、do~while语句、continue语句、break语句、goto语句和return语句。但在介绍这些控制语句之前,首先介绍一下输

入输出语句。3.4.1C++的输入输出•C++中除了可以使用C语言中的scanf和printf函数进行输入输出外,还提供了标准的输入输出流。例如,从键盘输入时需要用到输入流,在显示器上面显示信息需要输出流。其中,cin代表输入

流,cout代表输出流。它们是在头文件iostream中定义的。因此,在使用输入输出流的时候,需要引入iostream头文件。3.4.1C++的输入输出•1.输入语句•C++的输入语句用cin表示。其中,cin必须和“>>”一起使

用。使用cin的语法代码如下:•例如,想要从键盘输入一些数据,将使用下述代码:•如果想要一次性输入多个数据,不是使用逗号作为分隔符,而应该用“>>”分隔,应该写成:3.4.1C++的输入输出•2.输出语句•C++的输出语句用cout表示。其中,cout必

须和“<<”一起使用,使用cout的语法代码如下:•例如,想要从键盘输出一些数据,将使用下述代码:•如果想要一次性的输出多个数据,同样不是使用逗号作为分隔符,而是每项数据之间用“<<”分隔,如上述代码所示。•注意:在C++中,可以不用“\n‖控件换行,可以使用“e

ndl‖进行换行。因为在头文件iostream中定义endl(endofline)代表回车换行,其作用与“\n‖相同。3.4.2选择语句•选择语句也称分支语句,即根据不同的条件执行不同的语句。在C++中,主要的选择语句有if语句和switch语句。•1.if语句•if语句

有三种基本结构。•(1)第一种基本结构的语法如下:•执行该if语句时,首先判断表达式是否正确。如果正确,则执行语句1。如果不正确,则不执行任何操作,执行if语句后的其他语句。该语句流程如右图所示。3.4.2选择语句•(2

)第二种基本结构的语法如下:•执行该if语句时,首先判断表达式结果是否为真。如果判断结果为真,则执行语句1。如果判断结果为假,则执行语句2。该语句流程如右图所示。3.4.2选择语句•(3)第三种基本结构的语法如下:•执行该if语句时,首先对表达式1进

行判断。如果判断结果为真,则执行语句1。如果判断结果为假,则对表达式2进行判断。如果表达式2的判断结果为真,则执行语句2。否则,执行语句3。该语句的流程图如右图所示。3.4.2选择语句•2.switch语句•C++中的另外一种选择语句是s

witch语句,其语法如下:该语句的流程图如右图所示。3.4.3循环语句•在编写程序的过程中,经常会遇到一些许多有规律性的重复操作,则在程序中需要重复执行这些语句。为此,C++中提供了循环语句,可使代

码大大简化。循环语句包括循环条件和循环体两部。C++中的循环语句包括for语句、while语句和do~while语句。•1.for语句•for语句是使用最频繁并且最灵活的循环语句。其语法代码如下:•其中,表达式1通常用于为循环变量赋初值,表达式2为循环条件,表达式3用于循环变

量的递增或是递减,使得循环趋于结束。3.4.3循环语句•for语句的执行过程如下:•(1)初始化表达式。•(2)执行循环表达式。如果不满足条件,则跳出循环语句。•(3)如果满足条件,则执行语句块(循环体)。•(4)循环变量递增或递减。•(5)返回步

骤(2)。•(6)循环结束,执行for语句下的语句。•for语句的流程图如右图所示。3.4.3循环语句•2.while语句•while语句用于“当满足某一条件时进入循环”的情况,其语法代码如下:•while语句的执行过程如下:•(1)判断循环条件。•(2)表达式如果

为真,则进入循环体。否则,进入步骤(4)。•(3)执行循环体表达式。•(4)循环结束,执行while语句下的语句。•while语句的流程图如右图所示。3.4.3循环语句•3.do~while语句•do~while语句和while语句类似,其语句代码

如下:•do~while语句的执行过程如下:•(1)执行循环体语句块。•(2)判断循环条件。如果满足条件,返回步骤(1)。否则,执行步骤(3)。•(3)退出do~while语句。•do~while语句的流程图如右图所示。3.4.3循环语句•从上述代码中可以看出,while语句和do~

while语句的区别:•(1)从结构上看,while语句的循环条件在前,循环体语句块在后。而do~while语句的循环体语句块在前,循环条件在后。•(2)while语句的循环条件后没有分号,而do~while语句的循环条件后有分号,且不能省略。•(3)从执行流程

上看,while语句如是不满足循环条件,会直接跳过该循环。而do~while语句是无条件地执行一次循环体语句块。3.4.4其他语句•在循环语句中,经常用到一些语句,如break、continue和goto语句等。下面对这些语句做简单介绍。•1.continue语句•c

ontinue语句是跳过循环体中剩余的语句而强制执行下一次循环。其作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定。•continue语句只能用于循环语句中,通常与if语句配合使用。•2.break语句•当程序

运行到break语句时,立即结束break语句所在的整个循环,转向循环语句下面的语句继续执行。•3.goto语句•goto语句为无条件转向语句,通常与条件语句配合使用。但其易使程序流程混乱,一般不建议使用。所以在此不过

多介绍。第4章C++的面向对象•面向对象技术是当今软件开发的主流,很多开发人员都转向使用面向对象的语言进行编程。而C++也是面向对象程序设计语言的一种。本章将依次讲解C++中的类与对象。内容包括类与对象

、类的继承与派生、类的多态性等。通过本章学习,读者能够熟练掌握面向对象的思想,并能将这种思想融入到代码中。4.1类与对象•客观世界中,任何一个事物都可以看成是一个对象(Object)。在C++中,对象的类型被称为类(class)。本节将具体介绍类与对象的相关概

念与使用。4.1.1面向对象思想•面向对象的思想和面向过程的思想是相对的。面向过程的程序,详细地描述了每一时刻的数据结构及对其的操作过程。而面向对象的程序,将一个个小的操作封装成对象。在以后的编写中,只需调用对

象即可。面向对象的程序有三大特性:封装性、继承性与多态性。4.1.1面向对象思想•1.封装性•所谓的封装性,有着两方面的含义:一是将基本数据和对此数据进行操作的过程和函数结合起来,形成一个对象,各个对象之间相互独立,互不干扰。二是对

象将对外界公开的是一个界面,将具体的细节隐藏起来,保证了数据的安全性。•2.继承性•继承性是面向对象程序设计中最重要的机制。通过继承机制,可以方便地利用一个已有的类(父类)建立一个新的类(子类)。新类不但可以继承已有类的属性和方法,还可以拥有自己特有的方法。•3.多态性•所谓多态性是

当不同的对象收到相同的消息时产生不同的动作。多态性是面向对象程序设计的一个重要特征,其增加了程序的灵活性。4.1.2类的声明•类是具有相同属性和相同方法的对象的集合。类由类头(classhead)和类体(classbody)组成。类头由关键字cl

ass和类名组成。类体是指花括号({})中的内容。类体由数据成员和成员函数组成。需要注意提,类的声明以分号结束。声明类的一般形式为:4.1.2类的声明•其中,关键字private、public和prot

ected称为成员访问限定符。用这些访问限定符来声明各成员的访问属性。•private:只能在本类中访问,在类外不能访问。•public:既能在本类中访问,又能在类外通过该类的对象进行访问。•protected:与private类似,不能被类外访问,但能被派生类的成

员函数访问。•说明:如果在类的声明中没有使用关键字,则系统将数据成员和函数默认为是私有的。4.1.3成员函数的定义•在C++中,定义成员函数可以在类中定义,也可以在类外定义。•如例4-1所示,display()是

在类中进行声明并且定义。而GetName()和GetAge()只是在类中有声明,并没有进行定义。这种函数的定义代码如下:•则在Student类中,函数GetName()和GetAge()的定义如下:4.1.4类与对象

的关系•在现实生活中,每一个实体事物都可以作为一个对象。例如,一部手机、一支铅笔、一本书等。但是有些对象是有着相似性的。•在C++中,将这些有着相似的对象归为一类(class)。类是对象的抽象,而对象是类的实例。在编写程序时,应该先声明一个类,再去实例化若干个同类型的对象。•在一个类

中,类的属性指的是类的数据成员,类的行为指的是类的方法。4.1.5对象的声明和实例化•类是一个抽象的概念,因此在程序中不能直接引用。而是将其实例化成为对象后,通过这个对象来对类进行相关的操作。对象的声明有3种方法。1.先声明类类型,后定义对象•这种声明的语法代码如下:•用这种方式

为Student类声明对象:•如果想要一次性为某个类声明多个对象,可以用逗号作为分隔符。2.在声明类的同时声明对象•这种声明的语法代码如下:•用这种方式为Student类声明对象:•如果想要一次性为某个类声

明多个对象,可以用逗号作为分隔符。3.不出现类名,直接声明对象•这种声明的语法代码如下:•如果想要一次性为某个类声明多个对象,可以用逗号作为分隔符。用这种方式为类声明对象:在声明对象之后,可以调用类中的方法

。例如,stu1为Student类的一个对象,使用该对象对Student类中的数据成员和函数进行调用。代码如下:4.1.6构造函数和析构函数•下面介绍C++中两个特殊的函数:构造函数和析构函数。•1.构造函数•当声明一个类的属性

时,可以不对其进行初始化,因为在C++中有专门的初始化函数对该属性进行初始化。这个专门用来处理对象的初始化的函数就是构造函数。•构造函数是一种特殊的函数。其作用是在完成对象的初始化的同时,将对象的属性初始化。构造函数不需要用户自己来调用它,在创建对象时,由系统自动调用。定义构造

函数的语法代码如下:4.1.6构造函数和析构函数•构造函数的特点如下所述。•构造函数名与类名相同。•构造函数不能指定返回值类型。•构造函数可以有参数,也可以没有参数。•构造函数不能被程序显示调用,只能由系统自动调用。•每个类都必须有一个构造函

数。如果在声明一个类时没有给出构造函数的定义,则系统会为该类自动添加一个默认的构造函数。该构造函数的参数列表为空,函数体也为空。类中各属性的值被指定为所属类型的默认值。•构造函数可以重载(有关重载的概

念,会在4.2节进行介绍)。也就是说,一个类可以有多个参数不同的构造函数。•注意:当用户为声明的类添加一个自定义的构造函数时,系统则不再为程序添加默认的构造函数。4.1.6构造函数和析构函数•2.析构函数•当一个对象的生命周期结束时,应当去释放该对象所占用的内存空间

。这时,系统会自动调用析构函数来进行一些清理工作。定义析构函数的语法代码如下:•析构函数的特点如下所述。•析构函数不允许有返回值。•析构函数不允许有参数。•一个类中有且只有一个析构函数。因此,析构函数不能重载。•对于一个对象

来说,析构函数是最后一个被调用的函数。4.2C++类的继承和派生•类的继承是指新的类从已有类中获得已有的特性,例如,数据成员、成员函数等。而从已有类产生新类的过程就称为派生。其中,已有类称为基类或父类,新的类称为派生类或子类。本节将具体绍有关继承与派生的有关知识。4.2.1派生类的声明•派生

类可以将基类中的已有的特性继承下来,也可以添加一些自己特有的新特性。声明派生类的语法代码如下:•从上述代码中可以看出,派生类Student不仅从基类Student中继承了Student类已有的数据成员和成员函数,还新添加了一些自己的数据成员和

成员函数。另外,本例中采用的是公有继承。4.2.2派生类的继承方式•一个类中,其成员的访问权限有public、private和protected之分。而类的继承方式也有三种:公有继承、私有继承和受保护继承。1.公有继承•在声明一个

派生类时,将继承方式设为public,则该种继承方式称为公有继承。使用公有继承方式派生出来的类称为公有派生类,其基类称为公有基类。•采用公有方式继承时,基类的公有成员和受保护的成员在派生类中是可以引用的,而其私有

成员则不能在派生类中引用。有关公有继承中成员的访问权限如下表所示。2.私有继承•在声明一个派生类时,将继承方式设为private,则该种继承方式称为私有继承。使用私有继承方式派生出来的类称为私有派生类,其基类称为私有基类。•采用私有方式继承时,基类的公

有成员和受保护的成员在派生类中相当于是私有成员。而其私有成员不能在派生类中引用。有关私有继承中成员的访问权限如下表所示。3.受保护继承•在声明一个派生类时,将继承方式设为protected,则该种继承方式称

为受保护继承。使用受保护继承方式派生出来的类称为受保护派生类,其基类称为受保护基类。•采用受保护方式继承时,基类的公有成员和受保护成员在派生类中相当于是受保护成员。而其私有成员不能在派生类中引用。有关受保护继承中成

员的访问权限如下表所示。4.2.2单一继承和多重继承•在C++中,继承分为两种:单一继承和多重继承。•1.单一继承•一个基类只能由一个派生类来继承,这种继承方式称为单一继承。单一继承关系所形成的是一种树形结构,如下图所示。4.2.2

单一继承和多重继承•2.多重继承•一个派生类不仅只能拥有一个基类,还可以拥有多个基类,这种继承方式称为多重继承。基于多重继承方式的派生类,同时拥有了多个基类的特性。多重继承的关系如下图所示。•如果已经声明了类A、类B和类C,而类D想要同时成为A、B、C三

个类的派生类,即类D是多重继承的派生类。代码如下:4.3C++的多态性•在面向对象的程序中,多态性是一个非常重要的概念。同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。4.3.1多态的分类•C++中的多态性是指同一个函数名称对应着功能类似的多个函数。

这些函数执行不同但是功能类似的操作,从而实现了“一个接口,多种方法”。C++中的多态性分为两种:静态多态性和动态多态性。•静态多态性是指在编译程序时,系统就能确定所调用的是哪一个函数。因此,静态多态性又称编译时多态性。静态多态性是通过函数重载和

运算符重载实现的。•动态多态性是指在程序运行的过程中才能动态地确定调用哪一个函数。因此,动态多态性又称运行时多态性。动态多态性是通过继承和虚函数实现的。4.3.2运算符重载•所谓重载,就是对一个事物赋予新的

含义。而运算符重载,就是对已有的运算符进行重新定义,赋予其另外一种功能,以适应不同的数据类型。运算符重载的语法代码如下:•在C++中,有关重载运算符的规则如下:•只能对C++中已有的运算符进行重载。•并不是C++中的所有运算符都能进行重载,运算符“.”、“::

”、“?:”等不能重载。•重载运算符不能改变操作数的个数。•重载运算符不能改变运算符的优先级。•重载运算符不能改变运算符的结合性。•重载运算符不能使用默认参数。4.3.3虚函数•虚函数是指在派生类中定义与基类同名的函数,但基

类中的该函数前有virtual修饰。声明虚函数的语法代码如下:•在C++中,当一个成员函数被声明为虚函数后,其派生类中的同名函数将自动成为虚函数。因此,在派生类中重新声明该虚函数时,virtual可以不加。但为了使程序看起来清晰,一般在派

生类中也加上virtual。4.3.4纯虚函数•在某些特殊情况下,需要使用纯虚函数。例如,有一个动物类,其中有一个名为睡觉的函数。而各种动物的睡觉方式是不同的。此时,可以将动物类中的睡觉函数声明为纯虚函数,而该动物类则成

为了抽象类。•声明纯虚函数的语法代码如下:•纯虚函数没有函数体。•语法代码后面的“=0”不是代表该函数的返回值为0。此种表达方式仅仅是通知系统,这是一个纯虚函数。•有些类不用来定义对象,而只是作为一个基

类去派生新的类,这种类称为抽象类。在抽象类中,可以使用纯虚函数。第5章菜单栏、工具栏和状态栏•菜单栏、工具栏和状态栏是窗口界面的重要组成部分。三者是用户与应用程序交互的桥梁。合理地使用这三者,可以让用户更好地使用软件,提高用户体验度。本章将依次讲解在VisualC++中如

何构建菜单栏、工具栏和状态栏。内容包括菜单的分类、使用下拉式菜单、使用弹出式菜单、编辑工具栏按钮、修改默认的工具栏等。通过本章的学习,读者会熟练地使用菜单栏、工具栏以及状态栏,并且可以根据自己的想法,设计出一个漂亮的窗口。5.1菜单的分类•菜单栏一般位于应用程序窗口的顶部,如下图所示。

•菜单是用于显示一组选项(即菜单项)的下拉窗口,用户可以从这些菜单项中进行选择。在Windows窗口中,菜单分为两种:下拉式菜单和弹出式菜单(又称为上下文菜单)。5.1菜单的分类•1.下拉式菜单•一般情况下

,在应用程序窗口顶部的菜单栏中,选择菜单中的某个选项,就会显示一个下拉式菜单。在上图所示的窗口中,当用户选择菜单栏中的某一选项(如文件)时,就会显示一个下拉菜单。下图中的左图所示的菜单即为一个下拉式菜单。•2.弹出式菜单•一般情况

下,右击应用程序窗口中的空白处,就会显示一个弹出式菜单。该菜单处于自由浮动状态。下图中的右图所示的菜单即为一个弹出式菜单。5.2使用下拉式菜单•在VisualC++中,当创建的基于单文档或多文档的程序时,提供了一个默认的菜单栏。它包括文件、编辑、查看、窗口和

帮助等菜单项。但是当创建一个基于对话框的工程时,VisualC++将不会为其提供菜单栏。本节将使用菜单编辑器定义一个自己的菜单栏,并将其添加到基于对话框的工程中。5.2.3移动和删除菜单项•如果在添加了某个菜单项后,有时觉得某些位臵可能不合适。如果想要移动的是菜单栏上的菜单项,则可以直接选

择想要移动的菜单项,将其进行左右拖动,放到预期的位臵即可,如下图中的左图所示。•如果想要移动的是下拉式菜单里的菜单项,则可以直接选择要移动的菜单项,将其进行上下拖动,放到预期的位臵即可,如下图中的右图所示。•如果想要删除某个菜单项,只需选择要删除的菜单项,按下【De

lete】键即可。5.2.4启用和禁用菜单项•如果一个新的菜单没有添加命令处理函数,应用程序会自动将其禁用。要启用这个菜单项,使用ClassWizard为其添加一个命令处理函数即可。但是如果想要根据应用程序,有选择的启用和禁用某个菜单项,可以用ClassWizard添加一个界面消息

处理函数。5.2.5标记菜单项•如果想要给一个菜单项添加选中标记,同样可以使用ClassWizard添加的界面消息处理函数。在处理函数中添加如下代码。如果m_nCheck为TRUE,则菜单项前有一个选中标记“√”。如果m_nCheck为FALSE,则菜单项前没有选中标记“√”。5.2.6添加快捷键

•在使用菜单命令时,不仅可以使用鼠标进行选择,也可以使用快捷键。例如,经常使用的复制操作可以使用【Ctrl+C】组合键,剪切操作可以使用【Ctrl+X】组合键,粘贴操作可以使用【Ctrl+V】组合键等。5.3使用弹出式菜单•一般在右击窗口时,会出现弹出式菜单。下拉

式菜单可以在图形化的界面下进行加载,而弹出式菜单需要用户手工编写代码动态地加载。如果想在程序中动态地创建弹出式菜单,就必须用到MFC中的菜单类CMenu。•CreatePopupMenu():创建一个空白的弹出式菜单。•AppendMen

u():将新的菜单项添加到指定菜单项的末尾。•TrackPopupMenu(UINTnFlags,intx,inty,CWnd*pWnd,LPCRECTlpRect=0):在指定的位臵弹出一个菜单。其中,参数nFlags用于指定一个屏幕位臵和一

个鼠标按钮标志;参数x、y用于指定弹出式菜单坐标的水平位臵和菜单顶部在屏幕坐标中的垂直位臵;参数pWnd用于标识拥有此弹出式菜单的窗口;参数LpRectet用于指向一个RECT结构或CRect对象,如果其值为NUL

L,则代表当用户在此弹出式菜单外单击时,该弹出式菜单将会消失。5.4工具栏的使用•工具栏是一个显示位图式按钮行的控制条,按工具栏上的按钮,相当于选择菜单栏上的菜单项,可以执行相应的命令操作。工具栏一般位于窗

口客户区菜单栏的下面,其也可以作为一个浮动的小窗口,任由鼠标拖动。5.4.2移动和删除工具栏按钮•如果想要移动某个工具栏按钮,可以将其选中,然后进行左右拖动,将其放到预期的位臵上即可。如果想要删除某个工具栏按钮,不能直接按下【Delete】键,这样做只能删除该按钮

上的位图。要想真正地删除工具栏上的按钮,将其选中并拖到工具栏之外,释放鼠标即可。5.4.3添加按钮消息响应函数•在为按钮添加完位图之后,运行程序时,工具栏上新添加的按钮处于灰色状态,即不可用状态。要想使用该按钮,就要为其添加消息响应函数。5.5状态栏的使用•状态栏

位于应用程序窗口的底部,可以随时为用户提供当前应用程序的状态信息,其既不接收输入消息,也不产生命令消息。5.5.1状态栏概述•当使用MFCAppWizard创建一个基于单文档的程序时,在应用向导的【MFCAppWizard-step4】选对话框中,默认选择

了【Initialstatusbar】选项,如图5.25所示。那么,在生成的应用程序中,就自动添加了一个工具栏,图示可以参考程序sample0501的运行结果。•默认的状态栏大体分为2个部分:提示信息窗格和指示器信息窗格。而指示器信息窗格又由3部分构成,

其分别用于显示CapsLock、NumLockt和ScrollLock键的状态。5.5.2修改默认的状态栏•如果想要添加一个信息提示器到状态栏中,首先必须要在字符串表编辑器保存状态栏中符合条件时显示的文本。然后,使用文本编辑器添加一行代码到CMa

inFrame类中,通知CStatusBar类创建了一个新的指示器。第6章对话框•对话框是Windows程序的一种资源。使用对话框上的各种控件,可以实现相应的功能,提供了人机交互性能。本章将依次讲解对话框中的模态对话框和非模态对话框。另外,还对通用对话框进行了介绍。通过本章

的学习,读者可以熟练地使用对话框资源,从而设计出各式各样的对话框。6.1消息对话框•消息对话框是最常用的对话框。消息对话框是通过函数MessageBox实现的,其函数原型如下:•hWnd:表示拥有该消息

对话框的父级窗口。如果为NULL,则表示该消息对话框没有窗口。•lptText:表示用于指向将被显示的字符串的指针。•lpCaption:表示用于指向该消息对话框标题的字符串的指针。•UType:表示该消息对话框的风格。

6.1消息对话框•其中,UType由显示图标和按钮类型组成。两个类型的参数之间用“|”分隔。图标的种类及其对应的参数如下表所示。•按钮类型及其对应的参数如下表所示。6.1消息对话框•【示例6-1】平时在使用计算机时,当修改某些设臵后,会弹出一些对话框,让用户确定是否真的修改。则该消息对话框的设计代

码如下:•运行结果如右图所示。•从上图中可以看出,该消息对话框中,默认选中了【是】按钮。该默认按钮可以进行设臵,如下表所示。6.1消息对话框•例如,将上述代码改成如下代码:•则运行结果如下图所示。6.2对话框的创建与使用•前面只是介绍过如何创建一个基于对话框图的工程,下面将

介绍如何创建一个新的对话框,以及如何使用对话框。6.2.1对话框的分类•对话框分为两种:模态对话框和非模态对话框。1.模态对话框•当一个模态对话框工作时,其他窗口将失去输入焦点。只有当该模态对话框关闭后,用户才能对

其他窗口进行操作。例如,MicrosoftWord中对图片进行操作时的【题注】对话框、【设臵图片格式】对话框和【插入超链接】对话框都是模态对话框,如下图所示。2.非模态对话框•而非模态对话框与模态对话框相反。当其工作时,点击其他窗口,被点击的窗口可以获得输入焦点。例如,Mic

rosoftWord中常用的【查找和替换】对话框,就是非模态对话框。2.非模态对话框•模态对话框与非模态对话框的区别:•(1)模态对话框由CDialog::DoModal函数创建,而非模态对话框由CDialog::Create函数来创建。•(2)CDialog::DoModal函数负责显示其所

创建的模态对话框,而非模态对话框需要调用CDialog::ShowWindow函数来显示对话框。•(3)CDialog::DoModal函数负责销毁其所创建的模态对话框,而非模态对话框必须调用CWnd::DestoryWindow函数来

关闭对话框。6.2.2创建模态对话框•在前面的章节中,已经介绍过如何创建一个基于对话框的工程。下面将创建一个基于单文档的工程,并为其添加一个模态对话框。这个过程大致分为4个部分:创建模态对话框、创建对话框类、向程序中添加代码、添加消息响应函数。6.3非模态对话框•创建非

模态对话框与创建模态对话框的方式不同。模态对话框通过DoModal()函数来创建,而非模态对话框通过Create()函数来创建。•非模态对话框的一个重要作用就是在传递数据。也就是说,将非模态对话框中的数据及时的反应到其他窗口。下面通

过实例向读者介绍如何创建与使用非模态对话框。6.4通用对话框•通用对话框是指在Windows程序中有着特定功能的对话框。用户不必设计这类对话框,因为Windows系统本身提供了支持这类对话框的函数。通过调用这些函数,可以启动标准对话框来进行文件的打开和保存、搜索和替

换、颜色选择、字体选择以及打印。6.4.1文件对话框•文件对话框(CFileDialog)可以实现Windows标准的打开和保存文件。也就是说,文件对话框包括两种:【打开】对话框,用于打开文件,如左图所示;【保存】对话框,用于保存文件,如右图所示。6.

4.1文件对话框•CFileDialog类的构造函数原型如下:•其中各参数解释如下。•bOpenFileDialog:表示文件对话框的类型。如果为TRUE,则为【打开】对话框。如果为FALSE,则为【保存】对话框。•lpszDefExt:表示默认

文件的扩展名。•lpszFileName:表示文件名。•dwFlags:表示文件操作标记。6.4.1文件对话框•lpszFilter:表示过滤器。所谓过滤器就是一个限定字符串,对参数进行限制。•pParentWnd:表示父窗口指针。•CFileDialog类提供了许多

用于获取文件信息的函数。•GetPathName():用于获取所选文件的完整路径。•GetFileName():用于获取所选文件名。•GetFileExt():用于获取所选文件的扩展名。•GetFileTit

le():用于获取所选文件的标题名。6.4.2颜色对话框•颜色对话框(CColorDialog)是帮助用户设臵颜色,如下图所示。•CColorDialog类的构造函数原型如下:6.4.2颜色对话框•其中各参数解释如下。

•clrInit:表示默认选择的颜色。•dwFlags:表示颜色选择标记。•pParentWnd:表示父窗口指针。•CColorDialog类提供了许多用于获取颜色信息的函数。•GetColor():用于获取当前选择的颜色。•GetSavedCustom

Colors():用于获取所选择的颜色。•SetCurrentColor():用于设臵当前的颜色。•OnColorOK():用于验证输入对话框的颜色。6.4.3字体对话框•字体对话框(CFontDialog)用于字体的设臵,如下图所示

。•CFontDialog类的构造函数原型如下:6.4.3字体对话框•其中各参数解释如下。•lplfInitial:用于初始化对话框中的字体设臵。•dwFlags:表示该对话框的风格。•pdcPrinter:表示一个打印机的对象。•pPa

rentWnd:表示父窗口指针。•CFontDialog类提供了许多用于获取字体信息的函数。•GetCurrentFont():用于获取所选文字的属性。•GetFaceName():用于获取所选文字的字体名称。•GetSize():用于获取所选文字的大小。•GetC

olor():用于获取所选文字的颜色。•GetWeight():用于获取所选文字的磅值。•IsUnderline():用于获取所选文字是否有下画线。•IsBold():用于获取所选文字是否为粗体。•IsIt

alic():用于获取所选文字是否为斜体。6.4.4查找和替换对话框•查找和替换对话框是CFindReplaceDialog类实现的。这两个对话框是通用对话框中比较特殊的两个,因为它们是非模态对话框。【查找】对话框如图6.27所示,【查找/替换】对话框如下图所示。6.4.4

查找和替换对话框•作为非模态对话框,必须用new操作符分配存储空间,再用Create()函数进行初始化,最后用ShowWindow()函数显示对话框。CFindReplaceDialog类中Create函数的原型如下:6.4.4查找和替换对话框•其中各参数解释如下。•bFindDialogOnl

y:表示对话框的类型。如果该值为TRUE,表示【查找】对话框。如果为FALSE,则为【查找/替换】对话框。•LpszFindWhat:表示要查找的字符串。•LpszReplaceWhat:表示用于替换的字符串。•DwFlag:标

志位,用于设臵对话框。它由一个或多个标志组成。•PParentWnd:表示父窗口指针。•CFindReplaceDialog类提供了许多用于获取查找替换信息的函数。•FindNext:查找下一个匹配的字符串。•GetFindString:返回查找到的字符串。•GetReplaceString:返

回替换的字符串。•ReplaceAll:确定是否全部替换。•ReplaceCurrent:确定是否替换当前字符串。•SearchDown:确定是否向下查找。6.4.5打印对话框•打印对话框(CPrintDialog

)有两种功能。它既可以实现Windows标准的【打印】对话框,如左图所示,还可以实现【打印设臵】对话框,如右图所示。6.4.5打印对话框•CPrintDialog类的构造函数原型如下:•其中各参数解释如下。•bPrintSetupOnly:表示是否

带设臵对话框。如果为TRUE,则创建的是【打印】对话框。如果为FALSE,则创建的是【打印设臵】对话框。•dwFlags:用于设臵对话框标记。•pParentWnd:表示父窗口指针。6.4.5打印对话框•CPrintDialog类提供了许多用于获取打印信息的函数。•Get

Copies:用于获取对话框设臵打印的份数。•GetDefaults:返回打印机默认的设臵。•GetFormPage:返回打印范围的起始页面。•GetToPage:返回打印范围的终止页面。•GetPrinterDC:返回所选打印设备的

HDC设备句柄。•PrintAll:确定是否打印全部内容。•PrintRange:确定是否打印指定范围的页面。•PrintSelection:确定是否打印当前选择的部分页面。第7章控件•在Windows应用程序中,控件也是人机交互的一个重要角色。例如,对话框中的按钮、下拉列表框、

编辑框等都是控件。在VisualC++中,MFC提供了许多控件类。通过这些封装好的控件类,用户可以方便地创建和使用控件。本章将依次讲解Windows中常用的标准控件。通过本章的学习,读者可以熟练地为对话框添加和使用控件。7.1VisualC++中的标准

控件•Windows操作系统有着大量的标准控件,控件随处可见。读者可以任意打开一个窗口或是对话框,可以看到各式各样的控件。7.1.1控件概述•VisualC++提供了许多常用的标准控件,如下表所示。7.1.2创建控件•在Vi

sualC++中,创建控件有两种方法:一种是静态创建,另一种是动态创建。•静态创建:所谓静态创建就是在对话框模板中利用控件工具栏,直接将控件拖动到对话框上。然后通过ClassWizard为控件添加变量

、消息响应函数。•动态创建:所谓动态创建,就是在编写代码时,利用控件对应的控件类构造一个控件对象,然后调用Create()函数来创建该控件。•说明:静态创建控件是常用的创建控件的方法。本章示例都是采用这种方法。•下面通过

一个示例,向读者介绍如何创建控件。7.2按钮控件•按钮控件可以说是最常用的控件。从表中可以看出,按钮分为三类:下压式按钮、单行按钮、复选框。下面分别对这几种按钮进行介绍。7.2.1单选按钮•单选按钮由一个圆形按钮和一个静态文本框组成。单选按钮可以有选中和未选中两种状态。当单

击它时,会在中间显示一个小黑点,否则为空。单选按钮经常是成组出现的,但一次仅可以选择一组中的一个单选按钮。7.2.2复选框•复选框由一个方框标记和一个静态文本框组成。同单选按钮一样,复选框也可以有选中和未选中两种状

态。当单击它时,会在中间一个对号,否则为空。复选框可以单个出现,也可以成组出现。当复选框成组出现时,可以选择一个或多个复选框。7.3静态控件•静态控件与编辑控件都可以显示文本信息,但静态控件一般不接受用户的输入,而编辑控

件允许用户进行输入操作。在控件工具栏中,静态文本(StaticText)、图片(Picture)和分组框(GroupBox)都是静态控件。这些静态控件可以用来显示静态的文本信息、图片及位图信息。7.3.1静态文本•静态文本控件在一般情况

下,仅仅用于显示一些文本信息。其通常与编辑框一同使用。例如,在设计一个用户登录对话框时,需要将静态文本和编辑框结合使用,如下图所示。7.3.2图片•在应用程序中适当地引入一些图片,会增加程序的美观性。这时,可以使用图片控件。7.3.3分组框•分组框又称选择组,其通常包

含一些单选按钮和复选框的组合,如下图所示。•其使用方法和静态文本有些类似,因此有关分组框的其他用法,在此不再具体介绍。7.4列表框控件与组合框控件•列表框控件从表面上看起来像是一个拉大的编辑框。其实,列表框控件的作用和组合框控件的作用是类似的,就是供用户选择

信息。下面对这两种控件分别进行介绍。7.4.1列表框(ListBox)•列表框中的内容通常是一系列的供用户选择的信息选项。当用户从中进行选择时,可以选择其中的一项,也可以选择多项。列表框的风格属性是通过其属性

对话框中Styles进行设臵的,其属性对话框如下图所示。7.4.1列表框(ListBox)•【Styles】选择卡中各属性的含义如下表所示。7.4.1列表框(ListBox)•列表框对应的MFC类为CListBox类。CListBox类封装了对列表控件进行

的各种操作。其中,常用的操作如下表所示。7.4.2组合框(ComboBox)•组合框是一个很简单的控件,它实际上是其他控件的一个集合。从表面上面,组合框是由一个编辑框和一个下拉列表框组合而成。用户可以从组合框预定义的列表中进行选择,也可以从编辑框中进行输入。组合框如左图中的主题所示。•

列表框的风格可以通过其属性对话框中的【Styles】选择卡进行设臵。列表框的属性对话框如右图所示。7.4.2组合框(ComboBox)•【Styles】选择卡常用属性及其含义如下表所示。7.4.2组合框(ComboBox)•在使用组合框控件时,可以直接在【Data】选择卡中为

其添加选项。•注意:当填写完一个选项,需要按下【Ctrl+Enter】键,再填写另一下选项。•组合框对应的MFC类为CComboBox类。CComboBox类封装了对组合框控件进行的各种操作。如果想要为组合框动态的添加选项,可以使用AddStri

ng()函数。AddString()函数的原型如下:•intAddString(LPCTSTRlpszString);•如果想要全部清除组合框中的内容,可以使用ResetContent()函数。ResetContent()函

数的原型如下:•voidResetContent();•在组合框的使用中,最常用的操作就是获取当前选项的内容。该操作需要用到以下几个函数。•GetDlogItem():获取组合框的指针。•GetCurSel():获取组合框当前选项的索引值。•GetLBText():获

取组合框当前选项的字符串。7.5树形控件与列表视图控件•树形控件(TreeControl)通常与列表视图控件(ListControl)结合使用。二者通过不同的方式向使用者显示信息。下面对这两个控件进行详细介绍。7.5.1

树形控件•树形控件(TreeControl)用于显示具有层次结构的数据。该控件有一个根结点(Root),根结点下面有许多子结点。每个子结点又可以作为父结点拥有自己的子结点。•树形控件的风格可以通过其属性对话框的【Styles】和【MoreStyles】选项卡进行设臵。树形控件的属性对话框如下

图所示。7.5.1树形控件•【Styles】和【MoreStyles】选择卡常用属性及其含义如下表所示。7.5.1树形控件•树形控件对应的MFC类为CTreeCtrl。CTreeCtrl类封装了对树形控件的大量操作

。如果想要获取一个树形控件中的选项的项目,可以使用GetCount()函数。GetCount()函数的原型如下:•UINTGetCount()const;•如果想要查看一个结点是否存在子结点,可以使用ItemHasChildren()函数。Ite

mHasChildren()函数的原型如下:•BoolItemHasChildren(HTREEITEMhItem);•如果想要获取一个树形控件指定项的子项,可以使用GetChildItem()函数。GetChildItem()函数的原型如下:•HTREEITEMGetChildItem(H

TREEITEMhItem)const;•如果想要获取一个树形控件指定项的父项,可以使用GetParentItem()函数。GetParentItem()函数的原型如下:•HTREEITEMGetPar

entItem(HTREEITEMhItem)const;7.5.1树形控件•如果想要获取一个树形控件的当前项,可以使用GetSelectItem()函数。GetSelectItem()函数的原型如下:•HTRE

EITEMGetSelectedItem()const;•如果想要获取获取一个树形控件指定项的根结点,可以使用GetRootItem()函数。GetRootItem()函数的原型如下:•HTREEITEMGetRootItem()const;

•如果想要获取一个树形控件指定项的文本信息,可以使用GetItemText()函数。GetItemText()函数的原型如下:•CStringGetItemText(HTREEITEMhItem)const;•如果想要设臵一个树形控件指定项的文本信息,可以使用Set

ItemText()函数。SetItemText()函数的原型如下:•BOOLSetItemText(HTREEITEMhItem,LPCTSTRlpszItem);7.5.1树形控件•如果想要在一个树形控件中插入一个

新项,可以使用InsertItem()函数。InsertItem()函数的原型如下:•HTREEITEMInsertItem(LPTVINSERTSTRUCTlpInsertStruct);•如果想要从一个树形控件中删除一个项,可以使用DeleteItem()

函数。DeleteItem()函数的原型如下:•BOOLDeleteItem(HTREEITEMhItem);•如果想要从一个树形控件中删除所有的项,可以使用DeleteAllItems()函数。Dele

teAllItems()函数的原型如下:•BOOLDeleteAllItems();•如果想要一个树形控件在生成时,将所有的结点都展开,可以使用Expand()函数。Expand()函数的原型如下:•BOOLExpand(HTREEITEMhItem,UINTnCode);7.

5.2列表视图控件•列表视图控件(ListControl)以列表的形式显示数据。通常情况下,在显示数据库中的记录时会用到该控件。列表视图控件的风格可以通过其属性对话框进行设臵。列表视图控件的属性对话框如下图所示。7.5.2列表视图控件•

列表视图控件中常用属性及其各属性的说明如下表所示。7.5.2列表视图控件•列表视图控件对应的MFC类为CListCtrl。CListCtrl类中封装了大量对列表视图控件的操作。•如果想要获取一个条目的状态,可以使用GetItemState()函数。该函数的原型如下:•U

INTGetItemState(intnItem,UINTnMask)const;•如果想要改变一个条目的状态,可以使用SetItemState()函数。该函数的原型如下:•BOOLSetItemState(intnItem,LVITEM*pIte

m);•BOOLSetItemState(intnItem,UINTnState,UINTnMask);•如果想要获取一个条目的文本,可以使用GetItemText()函数。该函数的原型如下:•intGetItemText(intnItem,int

nSubItem,LPTSTRlpszText,intnLen)const;•CStringGetItemText(intnItem,intnSubItem)const;•如果想要改变一个条目的文本,可以使用SetItemText()函数。该函数的原型如下:•BOOL

SetItemText(intnItem,intnSubItem,LPCTSTRlpszText);7.5.2列表视图控件•如果想要查找具有特定字符的条目,可以使用FindItem()函数。该函数的原型如下:•intFindItem(LVFINDI

NFO*pFindInfo,intnStart=-1)const;•如果想要将所有的条目排序,可以使用SortItems()函数。该函数的原型如下:•BOOLSortItems(PFNLVCOMPAREpfnCompare,DWORD_

PTRdwData);•如果想要插入新的一列,可以使用InsertColumn()函数。该函数的原型如下:•intInsertColumn(intnCol,constLVCOLUMN*pColumn);•intInsertColumn(intnCol,LPCTSTRlpszColumn

Heading,intnFormat=LVCFMT_LEFT,•intnWidth=-1,intnSubItem=-1);•如果想要删除一列,可以使用DeleteColumn()函数。该函数的作用如下:•BOOLDeleteColumn(intnCol)

;7.5.2列表视图控件•如果想要准备一个列表视图控件以添加大量的项,可以使用SetItemCount函数。该函数的原型如下:•voidSetItemCount(intnItems);•如果想要添加一个新的条目,可以使用InsertItem()函数。该函数的原型如下:•intIns

ertItem(constLVITEM*pItem);•intInsertItem(intnItem,LPCTSTRlpszItem);•intInsertItem(intnItem,LPCTSTRlpszItem,

intnImage);•intInsertItem(UINTnMask,intnItem,LPCTSTRlpszItem,UINTnState,•UINTnStateMask,intnImage,LPARAMlParam);•如果想要删除一个条目,可以使用Delet

eItem()函数。该函数的原型如下:•BOOLDeleteItem(intnItem);•如果想要将全部的条目删除,可以使用DeleteAllItems()函数。该函数的原型如下;•BOOLDeleteAllItems();7.6滑块控件与进度条控件•滑块控件和进度条控件都是向用户显示某

种事物的变化。但不同的是,滑块控件是通过手工拖动的方式来改变数据的大小,而进度条是自动向用户展示程序的进度。下面就对这两种控件进行详细介绍。7.6.1滑块控件•滑块控件(Slider)由刻度和“滑块”组成。用户可以通过鼠标或是

键盘手动对其进行控制。滑块控件的风格可以通过其属性对话框进行设臵。滑块控件的属性对话框如下图所示。7.6.1滑块控件•滑块控件中的常用属性及其说明如下表所示。7.6.1滑块控件•滑块控件对应的MFC类为CSliderCtrl。CSliderCtrl类封装了大量对滑块控件的

操作。•如果想要获取滑块移动一格时改变的数值大小,可以使用GetLineSize()函数。该函数的原型如下:•intGetLineSize()const;•如果想要设臵滑块移动一格时改变的数值大小,可以使用Get

PageSize()函数。该函数的原型如下:•intGetPageSize()const;•如果想要获取滑块上表示的数值范围,可以使用GetRange()函数。该函数的原型如下:•voidGetRange(int&nMin,int&nMax)const;•如果想要设臵滑块

上表示的数值范围,可以使用SetRange()函数。该函数的原型如下:•voidSetRange(intnMin,intnMax,BOOLbRedraw=FALSE);•如果想要获取滑块上刻度的最小值,可以使用GetRangeMin()函数。该函数的原型如下:•intGetRangeMin(

)const;7.6.1滑块控件•如果想要设臵滑块上刻度的最小值,可以使用SetRangeMin()函数。该函数的原型如下:•voidSetRangeMin(intnMin,BOOLbRedraw=FALSE);•如果想要获

取滑块上刻度的最大值,可以使用GetRangeMax()函数。该函数的原型如下:•intGetRangeMax()const;•如果想要设臵滑块上刻度的最大值,可以使用SetRangeMax()函数。该函数的原型如

下:•voidSetRangeMax(intnMax,BOOLbRedraw=FALSE);•如果想要获取滑块上选择部分的范围,可以使用GetSelection()函数。该函数的原型如下:•voidG

etSelection(int&nMin,int&nMax)const;•如果想要在滑块上进行选择,可以使用SetSelection()函数。该函数的原型如下:•voidSetSelection(intnMin,intnMax);•如果想要设臵滑块的当前位臵,可以使用SetPos()函数。

该函数的原型如下:•voidSetPos(intnPos);7.6.2进度条控件•进度条控件(Progress)用于显示程序的进度。进度条由一个范围和一个当前位臵组成。进度条控件的风格可以通过其属性对话框进行设臵。进度条控件的属性对话框如下图所示。7.6.2进度条控件•进度条控件对应的

类为CProgressCtrl。CProgressCtrl类封装了大量对进度条控件的操作。•如果想要获取进度条的范围,可以使用GetRange()函数。该函数的原型如下:•voidGetRange(int&nLower,int&nUpper);•如果

想要设臵进度条的范围,可以使用SetRange()函数。该函数的原型如下:•voidSetRange(shortnLower,shortnUpper);•voidSetRange32(intnLower,intnUpper);•如果想要获取进度条当前的位臵,可以使用G

etPos()函数。该函数的原型如下:•intGetPos();7.6.2进度条控件•如果想要设臵进度条当前的位臵,可以使用SetPos()函数。该函数的原型如下:•intSetPos(intnPos);•如果想要在进度条上偏移一定的位臵,可以使用OffsetP

os()函数。该函数的原型如下:•intOffsetPos(intnPos);•如果想要设臵进度条的步长,可以使用SetStep()函数。该函数的原型如下:•intSetStep(intnStep);•如果想要进度条以设臵的步长为单位向前进展

,可以使用StepIt()函数。该函数的原型如下:•intStepIt();第8章MFC常用类•MFC提供了大量封装好的类,其中有一些类不经常使用,但有一些类使用的频率要高一些。这些类用于处理字符串、日期和时间、文件操作以及异常等。本章将分别介绍这

些类,通过本章的学习,读者可以熟悉MFC的常用类,并熟练使用这些MFC常用类。8.1字符串类(CString)•CString类是一种用途广泛的数据类型。CString类简化了MFC中的许多操作,使得操作字符串更加方便。下面将详细介绍CStri

ng类对字符串的具体操作。8.1.1创建字符串对象•CString对象采用了动态分配内存的机制。也就是说,在创建CString对象时,不需对该对象指明内存大小,CString会根据实际情况动态地进行分配。创建一个CString类对象并为其赋值的

方法有以下几种方法。•第一种方法是先构造一个CString类的对象,然后再使用赋值语句为其赋值。•第二种方法是在构造CString类对象的同时,直接为其赋值。•第三种方法是在构造CString类对象的同时,利用引用来的值为其赋值。•第四种方法是在构造CStri

ng类对象的同时,采用单字符为其赋值。•第五种方法是在构造CString类对象的同时,产生了一个字符串。8.1.2CString类的成员函数•在CString类中,存在大量对字符串进行各种操作的函数。CSting类中常用函数及其说明如下表所示。8.1.3字符串的大小写转换•在实际操作中,经常

会将字符串进行大小写的转换。CString类中进行大小写转换的函数分别是:MakeUpper()和MakeLower()函数。其中,MakeUpper()是大写转化函数,MakeLower()小写转化函数。•【示例8-1】将指定字符串进行大写转换。

•运行程序,输出结果为“HELLO!”。•【示例8-2】将指定字符串进行小写转换。•运行程序,输出结果为“hello!”。•由上述两个输出结果看出,MakeUpper()和MakeLower()函数只对字母进行相应的转换,其他字符不会受到任何影响。8.1.4字符串的连接•进行字符串的连

接有两种方法:一是直接使用“+”进行直接连接,二是使用Insert()函数进行特殊的插入。下面通过实例对这两种方法进行具体介绍。1.“+‖连接字符串•使用“+”连接字符串是最常用的连接字符串的方法。•【示例8-3】使用“+”连接字符串。•运行程序

,输出结果str3为Hello,而str4为lloHe。由程序结果可以看出,使用“+”进行字符串连接时,是在“+”前面字符串的末尾加上“+”后面的字符串。2.Insert()函数•在连接字符串时,并不一定都是在字符串的末尾进行连接。此时,应该使用Insert函数。Insert()函

数有两种原型。•(1)第一种原型如下:•其中,nIndex用来表示插入字符串的位臵,ch表示将要插入的字符串。该函数的返回值int,表示改变后的字符串的长度。•(2)第二种原型如下:•其中,其中,nIndex用来表示插入

字符串的位臵,pstr表示需要插入的子链的指针。该函数的返回值int,表示改变后的字符串的长度。8.1.5字符串的比较•字符串的比较是根据字母对应的ASCII值。当两个字符串进行进行比较时,首先从第一个字符开始进行比较。如果两个字符

串的第一个字母的ASCII值相同,则比较第二个字母。依此类推,直到比较完为止。•在CString类中,比较字符串的函数有两个,分别如下:••和•其中,Compare()函数在比较时区分大小写,而CompareNoCase()函数则不区分大小写。8.1.6字符串

的提取•在CString类中,提取字符串可以使用Left()、Mid()、Right()3种函数。三者分别从左边、中间和右边开始提取字符串。3种函数的原型分别如下:•其中,nCount表示开始提取字符串的长度,nFirst代表要提取的开始索引位臵。8.1.7字符串的

查找•在CString中,Find()和ReverseFind()函数用于字符串指定位臵的查找。Find()函数是从一个字符串中查找字符串。Find()函数的原型有几中,分别如下:•其中,ch表示要查找的字符串,nStart表示开始查找的索引值

。Find()函数返回查找到的字符串的位臵。•ReverseFind()函数是从一个字符串的末尾开始查找字符。ReverseFind()函数的原型如下:•其中,ch表示要查找的字符。该函数的返回值是查找到的字符串的索引值。8.1.8字符串的移除•在CString类中,想要移除字符串可以

调用Remove()和Delete()函数。其中,Remove()用于从字符串中移除特定的字符。Remove()函数的原型如下:•其中,ch表示想要移除的字符。该函数的返回值是返回从字符串中移走的字符数。如果字符串没有变化,则返回0。8.1.9CString类的格式化•在前

面几个例子中,已经用到过Format()函数。该函数就是用于实现字符串的格式化。Format()函数的原型如下:•其中,fromat用于表示格式化字符串,args表示输出文本。•从上述代码分析,格式化字

符串由三部分组成:%、%前的表述性文字和%后各种格式字符。•在VisualC++中,%后的格式字符有以下几种,如下表所示。8.1.10CString类的类型转换•CString类型的数据可以转换成其他类型的数据。1.CString类型转换成整型

•如果将CString类型的数据转换成整型数据,可以使用atoi()函数。atoi()函数的原型如下:•【示例8-14】CString类型转换成整型。•运行程序,输出转换后的整型数据114。2.CStri

ng类型转换成char*类型•如果将CString类型的数据转换成char*odga,可以有多种方法。•使用GetBuffer()函数。GetBuffer()函数的原型如下:•【示例8-15】使用Ge

tBuffer()函数将CString类型转换类型。•使用memcpy()函数。memcpy()函数的原型如下:•【示例8-16】使用memcpy()函数将CString类型转换类型。8.2日期、时间类•在VisualC++中,有关日期和时间的类有两个:

CTime类和CTimeSpan类。CTime表示是的绝对时间,而CTimeSpan表示的是时间差。本节将分别对这个两个类进行详细介绍。8.2.1CTime类•CTime类计算的时间是从1970年1月1日之后的秒数。

由于CTime类没有基类,因此它可以在程序中任意位臵调用。通常情况下,构造一个CTime对象采用如下方式:•【示例8-18】构造一个CTime类对象。•上述内容表示的时间是2008年5月19日22时18分33秒。•如果想要获取当时系统时间,可以使用GetCurren

tTime()函数,代码如下:8.2.1CTime类•在CTime类中,还有许多和GetCurrentTime()类似的函数,如表8.3所示。•使用CTime类获取的当前系统时间可以转化成字符串,此时可以使用CTime类的Format函数。

Format函数的原型有以下两种:8.2.2CTimeSpan类•CTimeSpan类主要用于保存两个时间之间的间隔。CTimeSpan类的构造函数有以下三种:•其中,常用的是第三种方法。•【示例8-20】构造

一个CTimeSpan类的对象。8.2.1CTime类•在CTimeSpan类中,有一些常用的函数,如下表所示。8.2.3计时器•计时器的作用就是用于计时,当到达规定的时间点时,会触发事先设臵好的动作。SetTimer函数用于创建一个计时器,KillTimer函数用于销毁一个计时器。计时器属

于系统资源,使用完应及时销毁。•SetTimer函数的原型如下:•nIDEvent:表示计时器的ID。由于一个程序中可能存在多个计时器,因此用此ID号进行标记。•nElapse:表示时间间隔,单位是ms(毫秒)。•第三个参数看起来很复杂,一般情况下,

设臵其为NULL。8.2.3计时器•【示例8-24】使用SetTime函数创建一个计时器。•由于计时器属于系统资源,因此在使用完应该及时地销毁。在VisualC++中,使用KillTimer函数来销毁所创建的计时器。KillTimer函数的原型如下:•【示例8-25】使用KillT

imer函数销毁上例所创建的计时器。•读者通过阅读上面的内容可以发现,程序只是创建了计时器,而并没有说明计时器到达规定时间所做的操作。此时,应该使用OnTimer函数对计时器进行消息处理。8.3文件操作类(CFile)•VisualC++提供的CFile

类封装了对文件打开和关闭、读写和定位、删除、获取文件信息等操作。CFile类是最基本的文件操作类,它的派生类是CObject。本节将具体介绍CFile如何对文件进行操作。8.3.1文件的打开和关闭•CFile类有两种构造函数,

使用默认的构造函数创建一个CFile对象后,还需要使用Open()函数将文件打开。使用带参数的构造函数创建一个CFile对象的同时,就将该文件打开了。下面对这两种打开文件的方式分别进行介绍。1.使用默认的构造函数打开文件•CFile类默认的构造函数原型如

下:•如果在构造CFile类的对象采用这个默认的构造函数,那么仅仅创建了一个对象,而并没有打开文件。此时,需要用到Open()函数。Open()函数的原型如下:•lpszFileName:用于指定文件名。•nOpenFlags:指定打开文件的方式。该参数可以几个值的组

合,以“|”进行分隔。该参数的取值如下表所示。•pError:指向CFileException对象,用于异常操作。1.使用默认的构造函数打开文件2.使用带有一个参数的构造函数打开文件•CFile类有两个带参数的构造函数。其中,带一个参数的构造函数的原

型如下:•该构造函数已经打开了一个文件,参数hFile就是打开文件的文件句柄。该函数将新创建的CFile对象绑定到一个已经打开的文件句柄上。HFile可以赋值给CFile的成员变量m_hFile。HF

ile也可以使用CreateFile()函数打开。CreateFile()函数的原型如下:2.使用带有一个参数的构造函数打开文件•hTemplateFile:为GENERIC_READ访问的模式指定一个句柄到模板文件。•lpFileName:指向一个空结尾字符串。该参数指定了用于创建或

打开句柄的对象。•dwDesiredAccess:指定对象的访问方式。程序可以获得读访问权,写访问权,读写访问权或者是询问设备(devicequery)访问权。•dwShareMode:设臵共享模式。•lpSecurityAttributes:

指向一个SECURITY_ATTRIBUTES结构的指针用于确定如何在子进程中继承这个句柄。如果这个参数是NULL,则该句柄不可继承。•dwCreationDisposition:指定当文件存在或者不存在时如何动作。•dwFlagsAndAttributes:为文件指定属性和标志位。3.

使用带有两个参数的构造函数打开文件•CFile类的第二个带有参数的构造函数有两个参数,其原型如下:•lpszFileName:表示将要打开的文件的路径名。•nOpenFlags:表示文件的共享和存取模式。该参数与Open()函数的nOpenFlags参数完全相同。•【示例8-29】使用带有两个参数

的构造函数打开文件。8.3.3文件的读写•在CFile类中,有专门对文件进行读写操作的函数Read()和Write()。其中,Read()函数的原型如下:•lpBuf:表示接收数据缓冲区的指针。•nCount:指定读操作的字节数。•Write()函数的原型

如下:•lpBuf:表示接收数据缓冲区的指针。•nCount:指定写操作的字节数。8.3.3文件的读写•【示例8-30】对文件进行读写操作。8.3.4文件的定位•使用CFile类对文件进行读写操作时,可以进行随机读写。调用CFile类的Seek()函数,可以定位文件指针定位的位臵,而调用SeekT

oBegin()和SeekToEnd()函数可以来回移动文件指针。Seek()函数的原型如下:•lOff:指定从nForm位臵开始的字节偏移量。•nForm:指定文件的定位方式。•nForm的取值有以下几种。•CFile::begin:文件的开始位臵

。•CFile::current:文件的当前位臵。•CFile::end:文件的末尾位臵。•函数的返回值是文件定位后指针的绝对位臵。8.3.4文件的定位•SeekToBegin()函数的作用是将文件的定位指针移动到文件的开始位臵,其函数原型如下:•SeekToEnd()函数的作用

是将文件的定位指针移动到文件的开始位臵,其函数原型如下:•如果想要获取文件的定位指针所指向的位臵,可以使用GetPosition()函数。GetPosition()函数的原型如下:•如果想要获取文件的大小,可以使用GetLength()函数。GetLength()函数的原

型如下:•如果想要设臵文件的大小,可以使用SetLength()函数。SetLength()函数的原型如下:8.3.5文件的状态•使用CFile类的成员函数,可以对文件的状态进行相关操作。如果想要获取文件的状态,可以使用GetStatus()函数。GetStatus

()函数有两种原型,第一种函数原型如下:•rStatus:指向CFileStatus结构的指针。•CFileStatus结构成员如下表所示。8.3.5文件的状态•第二种函数原型如下:•lpszFileName:指定文件名。•rStatus:指向CFileStatu

s结构的指针。•【示例8-31】对文件进行读写操作。8.4异常类•在编写程序时,往往会发生一些意想不到的错误,这些错误被称为异常(Exception)。在VisualC++中,MFC封装了异常类CException。CException类是所有异常类的基类。8.4.1异常类简

介•在程序执行过程中,可能会产生3种情况:正常执行、错误执行和非正常执行。•正常执行:在正常执行的情况下,函数会正常执行并返回。•错误执行:在错误执行的情况下,函数可能会在参数的传递过程中发生错误,或是在不适当的上下文中引用。•非正常执行:在非正常执行的情况下,会超过程序的控件范围,从而影响函数

的执行情况。此时,所产生的非正常执行就是异常,应该将其进行捕获或是抛出。8.4.2文件异常操作•在进行文件操作时,经常会出现一些意外情况,如文件拒绝访问、未找到文件等,这些意外情况就被称作是文件操作异常。在VisualC++中,MFC提供了专门用于处理文件操作异常的类

CFileException。•CFileException类定义了3个成员变量用于描述异常原因,分别如下:•m_cause:int类型,用于记录异常的原因。该成员变量的取值及其说明如表8.7所示。•m_IOs

Error:long类型,用于记录操作系统I/O异常原因。•m_strFileName:CString类型,用于记录出错的文件名。8.4.3捕获异常•在进行异常捕获时,经常使用的语句是try~catch语句。该语句的语法格式如下:第9章多媒体技术•多媒体技术是一

种迅速发展的综合性电子信息技术,它给传统的计算机系统、音频和视频设备带来了方向性的变革,将对大众传媒产生深远的影响。由于在现代人们的日常生活中,图文、音频和视频文件发挥着越来越重要的作用。在应用程序中,也经

常需要处理各种各样的多媒体文件。本章主要介绍多媒体技术的使用,内容包括:图像处理技术、音频处理技术、视频处理技术。通过本章的学习,读者可以了解各种多媒体技术,而且还会对多媒体资源进行简单处理。9.1图像处理技术•图像是多媒体的一个重要类型,其无处不在。在W

indows中,有许多的图像文件。为了方便用户查看图像内容,系统提供了Windows图片和传真查看器。在计算机中,图像以多种形式进行存储,常见的图像格式有BMP、JPG、GIF和WMF等。本节介绍的图像处理技术,主要是以BMP

文件为例。9.1.1BMP文件结构•位图(BMP)文件是Windows中采用的图形文件格式,其扩展名为bmp、dib或rle。位图实际上是一个像素点的集合,它将图像中的每个像素的颜色进行存储,所以文件本身比较大。•在VisualC+

+中,位图文件由4部分组成:位图文件头、位图信息头、颜色信息表和位图像素数据。下面对这4部分分别进行介绍。1.位图文件头•位图文件头主要用于识别位图文件,其数据结构如下:•bfType:表示文件类型,必须为BM。•bfSize

:表示文件大小。•bfReserved1:保留,必须是0。•bfReserved2:保留,必须是0•bfOffBits:表示从文件头开始到图像数据的偏移字节数。2.位图信息头•位图信息头的数据结构如下:•b

iSize:表示结构的字节数。•biWidth:表示位图的宽度(像素数)。•biHeight:表示位图的高度(像素数)。•biPlanes:表示位面数,该参数必须设臵为1。•biBitCount:表示每个像素的位数。•biCompression:表示图像数据压缩的类型。

•biSizeImage:表示图像大小(字节数)。•biXPelsPerMeter:表示设备的水平分辨率。•biYPelsPerMeter:表示设备的垂直分辨率。•biClrUsed:表示实际用到的颜色表中的颜色数。•biClrIm

portant:表示显示位图所需的重要颜色数。3.颜色信息表•颜色信息表的数据结构如下:•rgbBlue:表示蓝色值。•rgbGreen:表示绿色值。•rgbRed:表示红色值。•rgbReserved:保留,必须为0。4.位图信息•由位图信息头和颜色信息表组成了位图信息。

位图信息的数据结构如下:•BmiHeader:表示位图信息头。•BmiColors:表示颜色信息表。5.位图像素数据•位图像素数据是位图的主体部分,存储了位图所有的像素的数据。根据不同的位图,位图数据所占字节数也是不同的。9.1.2在程序中显示BMP文件•为了

美化应用程序,在编写程序时,需要加载一些图片。例如,为一个对话框添加背景。但是使用图片控件显示图片时,显示图片的大小不能随着对话框的改变而改变。为了解决这一问题,可以使用CDC类中的StretchBlt()函数。StretchBlt()函数的原

型如下:•x:表示左上角横坐标。•y:表示左上角纵坐标。•nWidth:表示显示宽度。•nHeight:表示显示高度。•pSrcDC:表示设备环境指针。•xSrc:表示复制位图左上角横坐标。•ySrc:表示复制位图左上角纵坐标。•nSrcWidth:表示源位图宽度。•nSrc

Height:表示源位图高度。•dwRop:表示光栅运算操作类型。9.1.3在程序中显示JPEG和GIF文件•在VisualC++6.0环境中,没有提供对JPEG/GIF格式文件的支持。为了能在程序中显示JPEG和GIF文件,可以通过IPicture接

口来实现。•利用IPicture接口来操作JPEG/GIF图像文件时,只需要调用OleLoadPicture()函数加载图像信息,然后调用IPicture的Render()函数绘制并显示图像。•其中,OleLoadPicture()函数的原型如下:•pStream:表示指向包含有图像数据的流

的指针。•lSize:表示从流中读取的字节数。•fRunmode:表示运行模式。•riid:表示涉及到的接口标识,描述要返回的接口指针的类型。•ppvObj:表示在rrid中用到的接口指针变量的地址。•Render()函数的原型如下:•hdc

:表示设备环境句柄。•x:表示目标矩形区域左上角的x坐标•y:表示目标矩形区域左上角的y坐标•cx:表示目标矩形区域的宽度。•cy:表示目标矩形区域的高度。•xSrc:表示源图像的水平偏移。•ySrc:表示源图像的垂直偏移。•cxSrc:表示源图像上水平拷贝的数量。•cySrc:表示源图像上垂直

拷贝的数量。•prcWBounds:表示指向目标图元设备环境句柄的指针。9.2音频媒体•在音频文件中,WAVE格式的音频文件是比较常用的类型。本节主要介绍了如何在应用程序中播放WAVE资源和WAVE文件,并对调整音量大小进行了简单介绍。9.2.1播放WAVE资源

•在应用程序中播放WAVE资源时,需要用到以下几个步骤:•(1)调用FindResource()函数搜索指定的音频资源。该函数的原型如下:•hModule:表示资源模块句柄。•lpName:表示资源名称。•lpType:表示资源类型。9.2.1播放WAVE资源•(2)调用Loa

dResource()加载资源到存储器。该函数的原型如下:•hModule:表示资源模块句柄。•hResInfo:表示资源句柄。•(3)调用LockResource()函数获取资源句柄。该函数的原型如下:•LockResource:表示要锁定的资源句柄。9.2.1播

放WAVE资源•(4)调用sndPlaySound()函数播放WAVE资源。该函数的原型如下:•lpszSound:表示要播放的资源字符串。•fuSound:表示如何播放声音的标志。该参数的取值如下表所示。9.2.1播放WAVE资源•(5)调用Free

Resource()函数释放资源句柄。该函数的原型如下:•hglbResource:表示要释放的资源句柄。9.2.2播放WAVE文件•WAVE音频文件是比较常用的音频文件。在9.2.1节中,是当载入并获取音频资源的句柄后调用sndPlaySound()函数进行播放。其实,播放WAVE音

频文件的方式有多种,可以使用sndPlaySound()函数直接进行播放。•【示例9-4】使用sndPlaySound()函数直接进行播放。修改程序CSample0903Dlg中的OnPlay()函数。•运行程序sample0903

,和没有修改前的效果相同。9.3.3音量大小控制实现原理•音量控制是多媒体应用的主要部分。为了实现对音量的控制,微软提供了一些与Mixer(混音器)API函数。下面对音量大小控制实现过程做简单介绍。•(1)通过mixerOpen()函数,可以打开指定的

混音器设臵。该函数的原型如下:•phmx:返回已经打开的混音设备的标识句柄。•uMxId:指定要打开的混音器设备标识。•dwCallback:指定调用窗口句柄。•dwInstance:指定调用实例句柄。•fdwOpen:表示设备打开标志。•(2)调用mixerGetLineI

nfo()函数获取混音器设备指定的线路信息。该函数的原型如下:•hmxobj:用于指定音频线路的混音器设备对象句柄。•pmxl:表示MIXERLINE结构对象。•fdwInfo:用于指定要获取的信息标志。•(3)调用mixerGetLineControls()函数获取关联音频线路的一个或多

个控件器。该函数的原型如下:•hmxobj:指定要查询的混音器设备对象句柄。•pmxlc:表示MIXERLINECONTROLS结构对象。•fdwControls:指定要获取的信息标志。•(4)调用mixerSetC

ontrolDetails()函数获取指定控制器的详细信息。该函数的原型如下:•hmxobj:指定要查询的混合器设备对象句柄。•pmxcd:表示MIXERCONTROLDETAILS结构对象。•fdwDetails:指定要获取的信息标志。•(5)调用mixerSetControlDetails

()函数设臵指定控制器的详细信息。该函数的原型如下:•hmxobj:指定要查询的混合器设备对象句柄。•pmxcd:表示MIXERCONTROLDETAILS结构对象。•fdwDetails:指定要获取的信息标志。9.3.4音量调节器实例•了解了音量大小控制的实现原理后,下面通过一个

实例来实现对音量调节器的控制。9.3视频媒体•在视频媒体中,AVI文件是比较流行的影音文件。而Flash动画,在网络上也是随处可见。下面对在应用程序中如何播放AVI文件和Flash文件做简单介绍。9.3.1播放AV

I文件•如果想要在对话框中播放AVI文件,可以使用CAnimation控件。CAnimation的主要成员函数及其说明如下表所示。9.3.2播放Flash文件•如果想要播放Flash文件,可以使用ActiveX控件中的ShockwaveFlash控件。Shock

waveFlash的主要成员函数及其说明如下表所示。第10章DLL的开发与调用•Windows系统平台提供了一种有效的编程和运行环境,可以把独立的程序模块创建为较小的DLL(DynamicLinkableLibrary,动态链接库)文件,并可对它们单独编译和

测试。Windows操作系统的核心功能、系统服务、应用程序服务等多数是由一组动态链接库实现的。•使用动态链接库有很多优点。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这

些DLL模块可以同时被多个应用程序使用。而且当动态链接库中的函数改变后,只要不是参数改变,调用这个函数的应用程序并不需要重新编译,这在编程时是十分有用的。•通过本章的学习,读者会了解DLL的基本概念、能够使用VC实现几种类型的DLL的开发与调用操作。10.1DLL的基础知识•

在程序开发中,比较大的应用程序,往往被划分为很多模块,这些模块以二进制的方式提供,并能完成相对独立的功能。其中一些比较通用的模块,在构造其他软件系统时,也经常用到,就把这些模块汇集起来,形成“仓库”。而动态链接库(DLL)就可以看成一种“仓库”,提供可以直接使用的变量、函数或类。•在开发

和调用DLL之前,有必要了解一下动态链接库的相关概念。只有了解了动态链接库的结构和工作机制,才能灵活有效地开发和使用它。10.1.1DLL与LIB•与动态链接库(DLL)相对应的是静态链接库(LIB文件)。其相似之处是

它们都是将一部分可执行代码以及数据放在库中供用户程序使用,而且在使用时,这些代码就象是用户程序本身的一部分。而二者的主要区别就是在使用方法上。•使用静态链接库的应用程序从函数库(LIB)得到所引用的函数的执行代码,然后把执行代码放

进自身的执行文件中,这样,应用程序在运行时就可以不再需要静态函数库的支持。•使用动态链接库的应用程序只包括了用于从DLL中定位所引用的函数的信息,而没有函数具体实现,要等到程序运行时才从DLL中获得函数的实现代码。因此,使用了DLL的应用程序在运行时必须

要有相应的DLL的支持。10.1.1DLL与LIB•如果与应用程序连接的是静态链接库,每一个应用程序在编译过程中都必须拷贝一份库的代码,这样就造成了资源的浪费,增大程序本身的代码开销。而动态链接库在编译过程中并不连接应用程序,而当应用程序运行时连接,可

以与其他应用程序共享库中的函数和资源,减少了因重复拷贝而造成的应用程序的冗长和计算机资源的占用。•当对目标代码链接形成可执行文件时,静态链接形成的程序是完整的,即从一台机器复制到另一台机器就可以直接运行。相比而言,其程序的文件比较大。而动态链接生成的可执行程序在不同机器间迁移时

,必须要带着相应的库,否则就不能运行。相对而言,动态链接的可执行文件本身比闲•另外,静态链接库和动态链接库还有一个区别,那就是静态链接库中不能再包含其他的动态或静态链接库,而动态链接库中还可以再包含其他的动态或静态链接库。10.1.2DLL与EXE•DLL和EXE都是Wi

ndows下的可执行模块,在对应的文件结构上,它们也类似的:具有文件头,重定位信息表,导入动态库表等,另外,DLL作为供程序调用的服务者,主要用来提供输出变量和函数供别的程序调用。DLL文件中包含函数(或类)的执行代码还包含导出的函数表和变量表,表中包含了函数的名

字和函数的地址。DLL的结构可简单表示如下图所示。10.1.2DLL与EXE•在DLL被装入的时候,以及进程中创建线程的时候,Windows都会以不同的参数调用入口点函数,然后该函数进行某些初始化工作后返回,DLL的执行就停止了。

Windows并不为DLL创建单独的进程空间,而是将其装入共享地址,然后将其映射到不同的进程供进程调用,从而达到代码共享的目的。•DLL是一个相对独立的应用程序,它有自己的模块句柄和模块资源,多个应用程序可以同时使用一个DLL模块。不管有多少进程访问DLL,同

一个DLL在内存中仅仅只有一份。•EXE是DLL所提供服务的使用者,调用DLL中的输出的函数和变量,每一个EXE在运行的时候,Windows均为它创建单独的进程环境,包括进程地址空间,EXE就在它的地址空间中运行,对别

的进程是不透明的,因此也就无法为别的进程调用,因此在许多情况下只能使用DLL来实现某些功能。用户可能在应用程序中基于如下要求创建和使用DLL:10.1.2DLL与EXE•在不用的可执行文件(EXE)之间共享公共的程序。•在设计应用程序时

,将其拆分为各个相互独立的功能部件,便于以后对这些功能部件进行独立的升级操作。•使资源数据独立于可执行的应用程序之外,且又能够方便快捷地访问它。其典型使用例子就是为一个应用程序设计各种语言的DLL,每个DLL中都包含有指定语言的字符串表。在应用程序中就可以通

过调用不同语言的DLL,实现程序的不同语言版本。10.1.4DLL的动态链接方法•在开发DLL时,需要把一些函数或类导出,这些函数或类就被称为导出函数或导出类;而在使用DLL时,则要把DLL的导出内容导入到应用程序

中。在应用程序中访问DLL,实际上就是应用程序中的导入函数与DLL文件中的导出函数进行链接。有两种方式链接:加载时动态链接(也称为隐式链接)和运行时动态链接(也称为显式链接)。•加载时动态链接:由编译系统完成对DLL的加载和应用程序结束时DLL

卸载的编码(如还有其它程序使用该DLL,则Windows对DLL的应用记录减1,直到所有相关程序都结束对该DLL的使用时才释放它),简单实用,但不够灵活,只能满足一般要求。•运行时动态链接:由编程者用API函数加载和卸载DLL来

达到调用DLL的目的,使用上较复杂,但能更加有效地使用内存,是编写大型应用程序时的重要方式。有关这两种动态链接方法的具体应用在下面结合具体的DLL开发与调用时,再详细介绍。10.1.5DLL文件构成•一般而言,发布的动态链接库,需要包含三个文件:包含文件(.H)、导入库文件(.L

IB)和实际代码文件(.DLL)。•包含文件(.H)为后缀名为.H的头文件。在该文件中,定义了DLL能够导出的函数、数据结构或类的声明。在DLL开发时,包含文件所声明的内容需要在.CPP文件中实现,.CPP文件形成的源程序代码被封装在DLL中。当要使用DLL时,必须将DLL

的这个头文件添加到使用该DLL的源程序代码中。•导入库文件(.LIB)虽然与静态链接库文件扩展名一样,但它不同于静态链接库文件。导入库文件包含了调用使用DLL的一些隐式链接,即加载时动态链接的信息。当应用程序以隐含的方式链接DLL时,导入库文件中的信息,如函数名

、函数地址等,被拷贝到应用程序的代码中去。•实际代码文件(.DLL)包含有DLL的真正可执行代码。应用程序调用DLL运行时,.DLL文件中实际代码被动态装入运行。10.1.5DLL文件构成•一般情况下,使用VC开发应用程序需要加载DLL时,该DLL的包含文件(.H)和实际

代码文件(.DLL)都是必须要用到的,而如果采用加载时动态链接DLL的方式,则导入库文件(.LIB)也是必要的。而当工程被编译成可执行文件时,只需要.EXE文件和DLL的实际代码文件(.DLL)即可运行

程序。包含文件(.H)和导入库文件(.LIB)中的信息都被拷贝到了.EXE文件中,因此也就不再需要了。10.2Win32DLL的开发与动态链接•本节将结合具体的实例开发,讲解DLL开发与动态链接的相关知识点。由于Win32DLL不使用MFC类库,其导出的函数是标准的C接口,能被

非MFC和MFC的应用程序调用,因而其应用十分广泛。10.2.2从DLL中导出函数•在动态链接库中定义有两种函数:导出函数(exportfunction)和内部函数(internalfunction)。导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。使

用VC开发DLL,声明导出函数有三种方法:1.使用关键字_declspec(dllexport)•使用MFC提供的修饰符号_declspec(DLLexport),在要输出的函数、类或数据的声明前加上_declspec(DLLexpo

rt)的修饰符,表示输出。__declspec(DLLexport)在C调用约定、C编译情况下可以去掉输出函数名的下划线前缀。extern"C"使得在C++中使用C编译方式成为可能。在C++下定义C函数,需要加extern"C"关键词。用ex

tern"C"来指明该函数使用C编译方式,输出的C函数可以从C代码里调用。2.使用DEF文件导出函数•模块定义文件(.DEF)是一个或多个用于描述DLL属性的模块语句组成的文本文件,每个DEF文件至少必须包含以下模块定义语句:•第一个语句必须是LIBRARY语句

,指出DLL的名字;•EXPORTS语句列出被导出函数的名字;将要输出的函数修饰名罗列在EXPORTS之下,这个名字必须与定义函数的名字完全一致,如此就得到一个没有任何修饰的函数名了。•可以使用DESCRIPTION语句描述DLL的用途(此句可选)。•可以使用“;”

对一行进行注释(可选)。3.使用AFX_EXT_CLASS导出•MFC扩展DLL使用AFX_EXT_CLASS宏来导出类,链接这种DLL的应用程序或其他DLL使用这个宏来导入类。•AFX_EXT_CLASS宏如

果用于DLL应用程序的实现中,则表示输出;如果用于使用DLL的应用程序中,则表示输入。要输出整个的类,对类使用_declspec(_DLLexpot);要输出类的成员函数,则对该函数使用_declspec(_DLLexport)。•

对于上节创建的DLL,如果不采用关键字_declspec(DLLexport)的方法,即在头文件“Cal.h‖中,导出函数的声明不含_declspec(dllexport)关键字,而采用模块定义文件的方法,实现如下:3.使用AFX_EXT_CLASS导出•在项目中,执行“File‖→―

New‖菜单命令,在弹出的New对话框中,添加一文本文件(TextFile),命名为“CalDLL‖,在该文件中添加如下代码:•在DEF文件中的第一条语句必须是LIBRARY语句,该语句表明该DEF文件属于一个DLL,在LIBR

ARY之后是DLL的名称,这个名称在链接时将放到DLL的引入库中。•EXPORTS语句下列出了DLL的所有导出函数以及它们的顺序值。函数的顺序值不是必须的,在指定导出函数的顺序值时,在函数名后跟上一个@符号

和一个数字,该数字即导出函数的顺序值。如果在DEF中指定了顺序值,它必须不小于1,且不大于DLL中所有导出函数的数目。•此时编译程序,即可形成同样的DLL。10.2.4加载时动态链接DLL•与运行时链接的动态调用方式相对应的是加载时链接DLL。加载时链接的特点是由编译系统

完成对DLL的加载和应用程序结束时对DLL的卸载。该方法简单实用,但缺少灵活性。•在开发的应用程序中,使用加载时链接DLL时,需要将DLL的引入库(.LIB)文件与应用程序进行静态链接。引入库文件中包含DLL的各种输出资源(函数、类等)的链接信息,这些链接信息可以理解为指向DLL库文件

中的实际代码的指针。当应用程序执行时,这些链接信息被复制到执行文件中,EXE执行时,DLL被“自动”加载,对DLL资源的调用“自动”完成。下面通过一个实例具体讲解其实现。•与10.2.3节的实例相似,创建一个基于对话框的MFC工程,在对话框资源模板中,添加用于设臵长方体

的参数和显示计算结果的编辑控件,并添加相关的成员变量和计算按钮消息响应函数。下面重点介绍一下程序代码的设计。10.2.4加载时动态链接DLL•在对话框类源文件的顶部,添加代码实现加载DLL的导入库文件,并声明DLL的导入函数,如下:•此时,在计算按钮的响应函数中,就可

以直接调用SurArea函数和Volumn函数计算长方体的表面积和体积,实现代码如下:10.2.4加载时动态链接DLL•此时,直接编译程序,会发现有一个链接错误,提示不能打开DLL的导入库文件“Cal32DllDemo.lib‖,如下图所示。10.2.4加载时动态链接DLL•

这是因为程序中,#pragmacomment(lib,"Cal32DllDemo.lib")语句并没有指明导入库文件“Cal32DllDemo.lib‖所在路径,而程序默认为该工程所在目录,因此需要将10.2.1节开发DLL生成的“Cal32DllDemo.lib‖文件拷

贝到NewLoadCal32工程目录下。此时再编译工程即可成功完成。•另外,也可以不采用#pragmacomment(lib,"Cal32DllDemo.lib")语句,通过设臵VC++6.0编译环境的方法来实现。具体方法如下:•将引入库文件“Cal32Dll

Demo.lib‖复制到当前的工程目录下,在VC++6.0中执行“Project→Settings‖菜单命令,在弹出的“ProjectSettings‖对话框中打开“Link‖页面,在“Object/librarymodules‖中

添加DLL的导入库文件的名称,即“Cal32DllDemo.lib‖,并以空格与其他的导入库文件名之间用空格分隔。单击“OK‖按钮,即完成了当前应用程序的链接设臵,就可以编译链接应用程序了。•此时,使用“F5‖快捷键运行程序,会发现弹出提示框,提示没有找到相关的DLL文件“Cal32

DllDemo.dll‖。10.2.4加载时动态链接DLL•这是因为在采用加载时链接DLL方法,在应用程序开始运行之前,Windows操作系统会加载DLL。但在程序中,并没有指明要加载DLL的路径,因此Windows

按照一定的搜索顺序自动查找并加载DLL,搜索顺序如下:•(1)包含EXE文件的目录。•(2)进程的当前工作目录。•(3)Windows系统目录(如WINNT/system32)。•(4)Windows所在目录(如WINNT

)。•(5)在Path环境变量中所指定的一系列目录。•如果Windows没有找到相关的DLL文件,系统将显示对话框提示并终止程序的执行。因此,要程序正常运行,需要把需要链接的DLL文件拷贝到这些目录其中之一的下面。10.2.4加载时动态链接DLL•一般选择当前工作目录,

即把“Cal32DllDemo.dll‖文件拷贝到NewLoadCal32工程目录下。此时,程序即可正确编译运行,运行结果如下图所示。•显然,使用加载时链接DLL,调用DLL中的导出函数要比运行时链接

DLL方便得多。但在某些情况下必须在运行时链接DLL,尤其是当用户无法获取与DLL相对应的导入库(.LIB)文件时,只能采用运行时链接来调用DLL中的导出函数。10.2.5调试DLL程序•在DLL开发过程中,由于DLL文件本身不能执行,因此不能直接对DLL进行功能

调试,必须结合DLL的调用程序。VisualC++是开发和测试DLL的有效工具,可以有两种方法对DLL进行调试(测试)。1.在同一工作区内同时加载DLL工程和调用DLL的工程•如果同时具有DLL工程和调用DLL程序的源代码,可以将DLL工

程与调用DLL的工程放臵在同一个VC工作区内。这样只需要对调用DLL的程序进行调试,即在调用DLL中函数的语句处设臵断点,执行到断点后,按下快捷键【F11】,就单步进入DLL中的相关导出函数的实现代码。2.直接在V

C的DLL工程中进行调试•如果用户没有调用DLL程序的源代码,只有其程序的可执行文件,这时可以直接在DLL工程中对可执行文件调用的DLL导出函数进行调试。10.2.6使用Depends工具查看DLL的信息•用户在发布程序时,往

往需要获取编译的exe或者DLL中包含其他哪些DLL文件。同样,在调用DLL时,也经常需要观察DLL提供的接口信息。VisualC++6.0提供了Depends工具,使用它可以查看应用程序调用了哪些DLL

以及DLL提供的导出接口。•在系统菜单“开始”→“所有程序”→“MicrosoftVisualStadio6.0‖→―MicrosoftVisualStadio6.0Tools‖下,会发现Depends工具。双击启动De

pends,执行“File‖→―Open‖菜单命令即可打开要查看的DLL。这里打开10.2.4节创建的调用DLL的可执行文件“NewLoadCal32.exe‖,如下图所示。10.2.6使用Depends工具查看DLL的信息•从中

可以发现,“NewLoadCal32.exe‖可执行程序调用了CAL32DLLDEMO.DLL和其他一些与MFC和系统相关的DLL。另外,还可以查看各DLL(如CAL32DLLDEMO.DLL)提供的导出函数接口信息。10.3MFC常规DLL的开发与链接•使用VC6.0除了前面介绍的开发Wi

n32DLL外,还可以开发MFCDLL。可开发的MFCDLL包括MFC常规DLL和MFC扩展DLL。MFC常规DLL可以在DLL的内部使用MFC类库,但其与客户程序的接口不能使用MFC。MFC常规DLL

可以被所有支持DLL技术的语言编写的应用程序调用。本节将具体介绍MFC常规DLL的开发与链接。10.3.1开发使用MFC类库的MFC常规DLL•在10.1.3节已经介绍过,MFC常规DLL有两种类型:静态链

接到MFC库的常规DLL(RegularDLLwithMFCstaticallylinked)和动态链接到MFC库的常规DLL(RegularDLLusingsharedMFCDLL)。从开发角度讲,两者的开发过程一样的,只不过最终

形成的DLL使用MFC库的方式不一样。10.3.2DLL的入/出口函数•正如同控制台程序需要main()函数、Win32程序需要WinMain()函数一样,每一个DLL必须有一个入口点,DLLMain是一个缺省的入口函数。DLLMain负责初始化(Initialization

)和结束(Termination)工作,每当一个新的进程或者该进程的新的线程访问DLL时,或者访问DLL的每一个进程或者线程不再使用DLL或者结束时,都会调用DLLMain函数。但是,使用TerminateProcess

或TerminateThread结束进程或者线程,不会调用DLLMain函数。10.3.2DLL的入/出口函数•Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它是DLL的

内部函数,所以不能直接在应用工程中引用,它是被自动调用的。DllMain的函数一般具有有如下结构:10.3.2DLL的入/出口函数•然而,在前面所创建的DLL并没有提供DllMain函数,但是仍然被应用程序成功地调用。这是因为当Windows找不到Dl

lMain函数时,系统会从其他库中引入一个不作任何操作的缺省的DllMain函数版本。•而每个MFC常规DLL都有MFCAppWizard自动生成的CWinApp派生类的对象,与其它MFC应用程序一样,它是在CWinApp派生类的成员函数InitInstance和ExitInst

ance完成初始化和终止的工作。实际上,MFC提供了一个基本的DllMain函数,在这种DLL中不必自己编写DllMain函数,由MFC提供的这个函数在装载DLL的时候调用InitInstance函数,而在DLL退出的时候调用Exit

Instance函数。所需要完成的初始化和终止的工作需要在这两个函数中完成。10.4MFC扩展DLL的开发与链接•不同于MFC常规DLL,MFC扩展DLL支持C++接口,即该DLL能够导出基于MFC的扩展类。应用程序通过链接DLL构造这些类的对象,也可以从这些类继承派生新的类。在实际

应用中,MFC扩展DLL比较常用,它一般用来包含一些MFC的增强功能,如扩展MFC的CButton、CMenu等类,使之具有更强大的功能。本节将具体介绍MFC扩展DLL的开发与链接。10.4.1开发扩展MFC类的MFC扩展DLL•在Vi

sualC++6.0中,可以通过创建MFC扩展DLL(MFCExtensionDLL)来实现从MFC派生的一些可重用类。通过这种方式可以扩展MFC所包括的内容,使得使用MFC编程更加的方便。此外,如果需要在应用程序和DLL之间传递MFC或者由MFC派生的对象

的指针,也必须使用MFC扩展DLL。第11章文档和视图•文档/视图结构是VisualC++基于文档开发应用程序的基础。通常情况下,文档用于存储数据,视图在框架窗口的工作区显示用户的数据并管理用户与数据的交互。文档和视图之间进行通信

从而得到和更新数据。本章将具体讲解文档/视图结构,内容包括文档/视图结构简介、文档/视图结构的应用、切分窗口和一档多视等。通过本章的学习,读者会熟练使用文档/视图结构。11.1文档/视图结构简介•应用程序的文档数据通过视图与用户交互。文档

可以看作是用于存储数据的容器,而视图可以看作是用于查看数据的窗口或是与数据发生交互的窗口。下面对文档/视图结构进行详细介绍。11.1.1文档/视图结构概述•文档/视图结构是一种新型的应用程序结构。文档/视图结构的

出现给广大程序员带来了很大的便利性,因为其大大简化了多数应用程序的设计开发过程。文档/视图结构的优点如下:•文档/视图结构将数据操作、数据显示和用户界面分开,使得模块划分更加合理,增加了模块的独立性。文档负责数据在存储,视图负责数据的输入

与用户的交互,二者各司其职,互不干扰。•文档/视图结构为用户提供了许多标准操作界面。例如,新建、打开、保存、关闭等。因此,用户不必为上述操作而重复书写代码。•文档/视图结构支持打印预览的功能。用户不必书写大量的代码来实现打印预

览的功能,而是书写少量代码来引用该功能。11.1.1文档/视图结构概述•文档的主要功能是用于存储数据,而视图是用户用于显示数据的可视窗口。二者的关系如下图所示。•每个文档都会有一个或多个视图显示,而一个视图只能对应一个文档。当文档中的数据发生改变时,视图中显示的数据也会随之发生变化。一个视图既可

以输出到窗口中,也可以输出到打印机上。11.1.2文档/视图结构分类•在VisualC++中,文档/视图结构的程序分为两类:单文档应用程序和多文档应用程序。1.文档应用程序(SDI)•在单文档程序中,用户在同一时刻只能操作一个文档。例如,Windo

w下的记事本就是典型的单文档应用程序。•当使用记事本操作文件时,如果想打开另一个文档,记事本会自动关闭当前的文档。如果当前文档内容有变化并未保存时,其会提示用户是否对所做的修改加以保存。1.文档应用程序(SDI

)•单文档程序一般只提供一个文件菜单。在该文件菜单下,一般有新建文档、打开文档、保存文档或另存为文档等子菜单,如下图所示。这类程序相对比较简单,常用的应用程序有终端仿真程序和一些工具程序。2.多文档应用程序(MDI)•在多文档程序中,允许用户在同一时刻操作多个文档。例如,V

iusalC++6.0集成开发环境就是一个多文档应用程序,如下图所示。2.多文档应用程序(MDI)•当使用ViusalC++6.0集成开发环境时,在界面中可以同时打开多个文件,而且为每个文件打开一个窗口。用户

可以通过切换活动窗口激活相应的文档进行编辑。•多文档应用程序也提供一个File菜单,用于新建、打开、保存文档。与单文档应用程序不同的是,它往往还提供提供一个Close(关闭)菜单项,用于关闭当前打开的文档。多文档应用程序还提供一个窗口菜单,管理所有打开的子窗口,包括对子窗口的新建

、关闭、层叠和平铺等。关闭一个窗口时,窗口内的文档也被自动关闭。11.2文档/视图结构应用程序框架分析•在VisualC++6.0中,使用AppWizard可以方便地创建文档/视图结构的应用程序。下面对文档/视图结构应用程序的创建、实现过程和工作机制作简单介绍。11.2.2

单文档应用程序的执行过程•想要熟练地运用单文档应用程序,首先要了解该应用程序的执行过程。具体过程如下:•(1)应用程序首先创建了一个CWinApp应用程序的对象theApp。该对象是一个全局对象并且只有一个。在应用程序

中可以找到这个全局对象。•(2)在InitInstance()函数创建文档模板pDocTemplate。在动态创建文档、视图和框架的同时,将三者绑定到一起。11.2.2单文档应用程序的执行过程•(3)在InitInstance()函数中,将单文档模板pDocTemplate添加到文档

模板的链表中。•(4)初始化文档/视图框架。•(5)显示并刷新窗口,进入消息循环。11.2.3程序框架中的主要类及相互关系•从创建一个单文档的应用程序过程中可以看出,一个完整的应用程序由4个类组成:CWinApp类、CFrameWnd类、CDocument类、CVi

ew类。•CWinApp类:应用程序类。该类是应用程序的起点。•CFrameWnd类:框架窗口类。该类是应用程序的框架窗口,用于接收消息循环。•CDocument类:文档类。该类用于存储应用程序中的数据成员和成员函数。•CView类:视图类。该类主要是负责用户数据的输入和显示文档

的内容,打印和打印预览也是在CView类中完成的。•4个类之间的相互作用可以由以下成员函数直接体现。11.2.3程序框架中的主要类及相互关系•(1)通过全局函数AfxGetApp()可以获取CWinApp应用程序类的全局对象。该函数的原型如下:•(2)CWinApp类中

主要包含以下成员函数。•(3)CFrameWnd类中主要包含以下成员函数。11.2.3程序框架中的主要类及相互关系•(4)CDocument类中主要包括以下成员函数。•(5)CView类中主要包含以下成员函数。11.2.5多文档应用程序框架•多文档应用程序与单文

档应用程序具有许多相同的特点。二者最大的区别是,单文档应用程序在同一时刻只能打开一个文档,而多文档应用程序可以同时打开多个文档。虽然多文档应用程序可以同时打开多个文档,但这些文档只能在父窗口的范围内显示。

•创建多文档应用程序的过程与创建单文档应用程序的过程有些类似,只是在【MFCAppWizard–Step1】对话框中选择【Multipledocument】。11.3切分窗口与多视•当用户需要同时对文当的不同部分进行编辑时,经常会用到切分窗口。在应用程序中,有许多方

式来显示多视图,切分窗口是常用的技术之一。•切分窗口分为两种:动态切分窗口和静态切分窗口。动态切分可以将视图切分成任意的行列组合,而静态切分不能万完全达到静态切分的效果。静态切分可以对每个视图创建不同的CView类,而动态切分不

能。下面对这两种切分窗口的方法分别进行介绍。11.3.1动态切分窗口•动态切分窗口允许用户在任何时候都对窗口进行切分,即可以通过选择菜单项来对窗口进行切分,也可以通过滚动条中的切分框对窗口进行切分。•在VisualC++6.0中,切分窗口需要用到CSplit

terWnd类。从表面上看,CSplitterWnd类像是一种特殊的框架,每个窗口都被相同或是不同的视图所填充。当窗口切分后,用户通过鼠标可以移动切分条来调整窗口的相对尺寸。11.3.1动态切分窗口•创建动态切分窗

口一般使用Create()函数。该函数的原型如下:•pParentWnd:表示切分窗口的父框架窗口。•nMaxRows:表示创建的最大的行数。行数不大于2。•nMaxCols:表示创建的最大的列数。列数不大于2。•sizeMin:表示窗格的现实大小。•pContext:表示

大多数情况下传给父窗口。•nID:表示子窗口的ID号。11.3.2静态切分窗口•当静态切分窗口时,需要使用CreateStatic()函数。该函数的原型如下:•pParentWnd:表示切分窗口的父框架窗口。

•nMaxRows:表示创建的最大的行数。行数不大于16。•nMaxCols:表示创建的最大的列数。列数不大于16。•nID:表示子窗口的ID号。11.3.3一档多视•通常情况下,一个单文档应用程序就可以满足用户的需求。但有些

特殊情况,需要文档数据的多种显示方式。例如,一个学生的成绩单,既需要以文本的内容显示,又需要以图形的方式显示。此时,就应该用到一档多视。•在实现一档多视之前,首先应该了解一档多视程序中,文档与视图以及视图与视图之间的关系。例

如,视图如何得到文档中存放的原始数据,文档如何得到在视图中输入或修改后的数据,以及当一个视图中的内容发生变化时,其他的视图如何感知这一变化等。11.3.3一档多视•下面以一个学生的成绩单为例,来说明上述的几个问题。假设文档类CStude

ntDoc对应的成员变量为m_studentDoc,主视图类CMainView对应的成员变量为m_mainView,次视图类CSubView对应的成员变量为m_subView。•(1)当视图类(如CMainView类)想要得到文档类中存放的原始数据(m_studentDoc)时,可以使用如下语

句:•(2)当在视图类(如CMainView类)中输入或修改了文档中的原始数据,需要将文档中的原始数据进行赋值或重新赋值时,可以使用如下语句:•(3)当在视图类(如CMainView类)中输入或修改了文档中的原始数据,需要通过其他视图类(如CSubView类)时,可以使用如下语句:•(4)当

在视图类(如CMainView类)中输入或修改了文档中的数据并通知其他视图类(如CSubView类)后,其他视图类要做出相应的反应。例如,其他视图类想要读取新的数据,可以在该类的OnUpdate()函数中添加如下语句:11.3.3一档

多视•上述工作原理如下图所示。11.4在视图窗口中显示网页•在文档\视图结构的应用中,MFC中提供了多个视图类供开发者使用。其中,CHtmlView类可以用于在视图窗口中添加网页。•CHtmlView类提供的Nav

igate2()函数用于浏览网页,该函数的原型如下:11.4在视图窗口中显示网页•pIDL:表示ITEMIDLIST结构指针。•dwFlags:表示浏览网页的标识。•lpszTargetFrameName:

字符串指针,表示显示资源的框架名称,默认为NULL。•lpszURL:字符串指针,表示一个URL。•lpszHeaders:表示发送到HTTP服务器的标题。这些标题被添加到默认IE浏览器的标题中。如果lpszURL不表示一个HTTPURL,该参数将被忽略。•lpvPostData:表示使用H

TTP提交事物时发送的数据,类型为无符号指针。•dwPostDataLen:表示lpPostData参数的长度。•baPostedData:表示使用HTTP提交事物时发送的数据,类型为CByteArray。第

12章数据库编程•数据库技术是当今世界范围内最为热门的一大技术。通常情况下,数据库是网络计算的后台支柱。因此,数据库技术无论在现在还是在将来都是一门不可或缺的商业应用技术。运用VisualC++中的数据库编程,可以方便地管理数据库中的数据。本章向读者介绍数据库编程技术,内容包括数据库基础、O

DBC数据库编程、ADO数据库编程。•通过本章的学习,读者可以熟练地使用数据库编程技术管理数据库中的数据。12.1数据库基础•掌握数据库技术,需要了解与数据库技术相关的4个基本概念:数据、数据库、数据库管理系统和数据库系统。1.数据•所谓数据,就是描述事物的符号

。在人类的日常生活中,数据无所不在。数字、字母、文字、图片、声音等都是数据。在VisualC++中,各种变量、常量都是数据。2.数据库•数据库,顾名思义,就是用于存储数据的地方。在计算机中,数据以一定的格式和规则存放在存储设备上。另外,在计算机中,数

据库是数据和数据对象的集合。所谓数据对象,就是指的表(Table)、视图(View)、存储过程(StoredProcedure)、触发器(Trigger)等。3.数据库管理系统•数据库管理系统(Database

ManagementSystem,DBMS)是一种管理和操作数据库的软件,主要用于建立、使用和维护数据库。例如,Oracle、Access、Sybase和SQLServer等都是DBMS。DBMS对数据进行统一的管理,以

保证数据的安全性和完整性。从计算机软件系统的构成来看,DBMS是介于用户和操作系统之间的一组软件,如图下所示。4.数据库系统•数据库系统(DatabaseSystem,DBS)是由计算机硬件、操作系统、数据库管理系统以及在它支持下建立起来的数据库、应用程序、用户和维护人

员组成的一个整体。目前使用最广泛的数据库系统是关系型数据库。12.4结构化查询语言(SQL)•SQL(StructuredQuaryLanguage),即结构化查询语言。SQL语言结构简洁,功能强大,

简单易学。因此,SQL语言得到了广泛应用。下面对SQL语言进行详细介绍。12.4.1SQL语言的分类•根据SQL语言的执行功能,可以将其分为4类。•1.数据定义语言•数据定义语言(DataDefinition

Language,DDL)完成数据的定义。DDL主要用于创建、修改和删除表、视图和索引。•2.查询语言•查询语言(QuaryLanguage,QL)完成数据的查询功能。QL主要用于单表查询、连接查询、嵌

套查询以及集合查询等不同的查询操作。•3.数据操纵语言•数据操纵语言(DataManipulationLanguage,DML)完成数据库中添加、修改、删除存储在数据库中的数据对象。•4.数据控制语言•数据控制语言(DataControlLanguage,DCL)用于控制访问数据库

的用户,还可以控制用户对数据库的访问类型。另外,DCL还可以用于对数据库的监控。12.4.2SQL语言的数据类型•数据库的基本组成单位是表,而表又是由数据组成的。表中每个字段的数据都需要说明它的数据类型。SQL语言中常用的数据类型如下表所

示。12.4.3SQL语句•在查询分析器中,通过使用SQL语句,同样可以创建、修改和删除数据库中的数据对象。打开查询分析器的方法和打开企业管理器的方法一样。在开始菜单中选择所有程序,单击【MicrosoftSQLServe

r】|【查询分析器】命令,即可弹出查询分析器的窗口。下面对常用的SQL语句进行简单介绍。1.创建表•如果想要创建一个数据库表,可以使用如下语句:•其中,限制条件的关键字可以是null(允许为空)、notnull(不允许为空)、unique(唯一)等。•【示例12-2】创建一个学生表,其由学

号、姓名、性别、年龄和住址5个字段组成。其中,学号应该是唯一的,且不能为空。SQL语句如下:2.向表中插入数据•如果想要向数据库表中添加一行新的数据,可以使用如下语句:•【示例12-3】向创建的学生表中插入数据,SQL语句如下:•注意:向表中插入内容时,如果是字符串类

型,需要用单引号。3.查询表中数据•如果想要对表中的数据进行查询,可以使用如下语句:•【示例12-4】对学生表中的数据进行查询,SQL语句如下:4.修改表中数据•如果想要修改一个数据库表中的数据,可以使用如下语句:•【示例12-5】向学生表中插入新的一列“爱好”,然后

对表中的内容进行修改SQL语句如下:5.删除表中数据•如果想要删除表中的一行或多行记录,可以使用如下语句:•【示例12-6】删除学生表中一条记录,SQL语句如下:•如果想要删除一个数据库表中所有的记录,可以使用如下语句:•【示例12-7】删除学生表中的所有记录,SQL语句如下:6

.删除表•如果想要删除一个表,可以使用如下语句:•【示例12-8】删除数据库中的学生表,SQL语句如下:7.视图的相关操作•视图(view)是一种从一个或几个基本表中根据用户需要而做成一个虚表。由于视图是虚表,因此它在存储时只存储视图的定义,而没有存储对应的数据。视图只在刚刚

打开的一瞬间,通过定义从基表中搜集数据,并展现给用户。•如果想要创建一个视图,可以使用如下语句:•【示例12-9】创建有关学生平均年龄的视图,SQL语句如下:•如果想要删除一个视图,可以使用如下语句:•【示例12-10】删除有关学生平均年龄的视图,SQL语句如下:

12.5数据库开发技术简介•MFC中封装了支持数据库操作的类,因此大大简化了在VisualC++集成开发环境下的数据库编程。数据库开技术有多种,ODBC、DAO、ADO等。下面分别对这三种技术进行介绍。12.5.1ODBC技术•ODBC是Op

enDatabaseConectivity(开放数据库互联)的缩写。ODBC提供了一组标准应用程序编程接口API,用户通过SQL语句实现对数据库管理系统的访问。任何应用程序同路人要系统包含某种数据库的ODBC驱动程序,

就可以使用API访问数据库,每个不同的数据源都由一个ODBC驱动支持。用户使用ODBC环境中的驱动程序管理器,实现驱动程序与DBMS的连接。ODBC的结构是分层管理的,其体系结构如右图所示。12.5.2DAO技术•DAO是DataAccessObject(数据访

问对象)的简称。DAO是第一个面向对象的接口,其通过MicrosoftAccess自动获得MicrosoftJet数据库引擎,进而可以访问数据库。•DAO技术通常用于操作.mdb数据库文件,但其可以使用MicrosoftAccess/Jet数据库引擎来访问ODB

C数据源。DAO的体系结构如下图所示。12.5.3ADO技术•ADO是ActiveXDataObject(ActiveX数据对象)的简称。ADO是一种特殊的OLEDB客户程序,它完全继承了OLEDB技术的优点,但又对OLEDB的接

口作了封装。ADO技术是一种高层的访问技术,并且是跨平台使用的。ADO允许访问VisualC++、VisualBasic、Delphi等开发语言。ADO的体系结构如下图所示。12.6ODBC数据库编程•ODBC是开放数

据数据库互联标准协议,它是Microsoft客户/服务器环境下的数据访问标准。在Win32环境下,用户可以使用ODBC数据源管理器获得对数据库的访问权限。12.6.1设臵ODBC数据源•由于VisualC++应用程序与创

建数据库表的数据库管理系统是两个不同的操作平台,要实现应用程序对数据库的访问,就必须要为数据库表选择适当的数据驱动程序,将VisualC++环境中的对数据库的操作转化成数据库系统可理解的操作。为了实现这个转换,Windows系统向用户提供了一个极为简便的接口——ODBC数据源

。12.6.2连接数据库•使用ODBC驱动程序时,应用程序对掌握库的访问需要用到MFC类中的CDatabase和CRecordset。其中,连接数据库时使用的是CDatabase类,对数据库中的记录进行操作时使用的是CRecords

et类。CDatabase类封装了建立数据库的连接、使用SQL语句对数据库数据进行操作及事务处理3个功能。1.建立数据库的连接•想要建立与数据源的连接,首先应构造一个CDatabase对象,然后再调用CDatabase的Open

成员函数。Open函数的原型如下:•lpszDSN:表示数据源名。•bExclusive:表示是否独占数据源。由于已连接的ODBC数据源是被多用户共享的,因此该参数默认为FALSE。•bReadOnly:设臵数据库数据是否为只读

数据。如果该参数为TRUE,则数据库的数据为只读状态,用户不能对数据库的数据进行更新操作。•lpszConnect:表示连接数据源的字符串。该字符串包含数据源名、用户帐号(ID)和口令等信息。其中,字符串中的“

ODBC‖表示要连接到一个ODBC数据源上。•bUseCursorLib:表示是否加载ODBC光标库。如果为TRUE,则会加载光标库,否则不会加载。其中,快照需要加载光标库,动态集不需要加载光标库。•使用Open函数连接数据库时,如果连接成功,函数返回非0,否则返回0。

2.使用SQL命令操作数据库•想要使用SQL命令操作数据库,可以使用CDatabase类的ExecuteSQL()函数。该函数的原型如下:•lpszSQL:表示指向字符串常量的指针或是字符串常量。该参数包含了有效的字符串命令。•【示例12-12】使用SQL命令

查询数据库中内容。3.CDBException类•在使用CDatabase类对数据库进行操作时,如果产生异常情况,会由CDBException类自动捕获异常。使用CDBException类捕获异常时,一般使用如下形式:12.6.3记录集的建立和关闭•使用

记录集对应的类CRecordset可以对数据库表记录进行添加、删除、修改和查询等操作。想要使用CRecordset类,首先需要建立数据库的连接,然后创建CRecordset类的对象,最后使用select命令打开动态记

录集。•CRecordset类中的成员变量如下:•m_hstmt:包含记录集ODBC句柄。•m_nFields:包含记录集中的字段数量。•m_Params:包含记录信中参数数据成员的数量。•m_pDatabase:指向数据源的CDatabase对象的指针。•m_strFilter:

表示一个过滤器,用于选择符合条件的记录。在select语句中起到where子句的作用。•m_strSort:表示一个字段排序依据。在select语句起到orderby子句的作用。12.6.3记录集的建立和关闭•CRecordset类中封装了对记

录集的操作。其中,CRecordset类中常用函数如下表所示。12.6.3记录集的建立和关闭•如果想要获取一个字段的值,可以使用GetFieldValue()函数。该函数的原型如下:lpszName:指定字段名。varValue:用于存储指定字段值的CDBVariant类型

的引用。nFieldType:指定字段类型。nIndex:表示基于0的字段索引值。strValue:将字段值转换为文本。12.6.4添加、删除和修改记录•使用CRecordset类,可以对数据库中的记录进行添加、删除和修改等操作。下面对这3种操作进行详细介绍。•1.添加记录•如果想要向记录集中

添加一条新的记录,可以使用AddNew()函数。该函数的原型如下:•2.修改记录•如果想要修改记录集中的数据,可以使用Edit()函数。该函数的原型如下:•3.删除记录•如果想要删除记录集中数据,可以使用Delete()函数。该函数的原型如下:1

2.7使用ADO操作数据库•在12.4.3节中已经了解到,ADO提供了双重接口,既可以在高级编程语言(例如,VisualC++、VisualBasic、Java等)中使用,也可以在各种脚本语言或一些宏语言中直接使用

。本章将重点介绍在VisualC++中,如何使用ADO操作数据库。12.7.1ADO基础•在使用ADO对数据库进行操作时,具体步骤如下:•(1)初始化OLE/COM库。•(2)使用Connection对象连接数据库。•(3)创建对该数据源的操作(例如,添加、删除、修改数据)。•(4

)执行该操作。•(5)把操作后的数据存储到可访问的对象中。•(6)更新数据源。•(7)关闭连接,释放对象。12.7.1ADO基础•在使用ADO对数据库进行操作时,常用的操作对象如下:•_ConnectionPtr:指向Connection连接对象的智能指针。•_Records

etPtr:指向的Recordset记录集对象的智能指针。•_CommandPtr:指向Command命令对象的智能指针。•_ParameterPtr:指向Parameter对象的指针。•FieldPtr:指向Fiel

d对象的指针。•ErrorPtr:指向Error对象的指针。•ProtertyPtr:指向Property对象的指针。12.7.2ADO编程方法•在使用ADO编程之前,先建立一个Access数据库test.mdb。下面介绍ADO编程的具体方法。•(1)创建应用程序。使用MF

CClassWizard创建一个基于对话框的应用程序,其他各选项均采用默认设臵。•(2)初始化OLE/COM库。在使用ADO操作数据库时,应该使用AfxOleInit()来初始化OLE/COM库。该函数原型如下:•在使用AfxOleInit()函数时,通常将其放在CWin

App::InitInstance()的重载函数中。12.7.2ADO编程方法•(3)引入ADO库文件。一般在工程的stdafx.h头文件里,直接使用预编译指令#import来导入ADO库文件。•其中,no_namespace表示ADO对象不使用名称空间。使用上述代码的作用是使系统自动生

成msado15.tlh、ado15.tli两个头文件来定义ADO库。值得注意的是,msado15.dll文件不一定在这个目录下,可按实际情况修改。另外,在编译的时候可能会出现如下警告,对此,微软在MSDN中作了说明,并建议开发人员不要理会这个警告。12.7.2ADO编

程方法•(4)创建ADO与数据源的连接。创建ADO与数据源的连接时,首先需要添加一个指向连接对象的指针,然后调用CreateInstance()来创建一个连接对象的实例。•创建指向连接对象的代码如下:•调用该对象的CreateIns

tance()方法,可以有两种方式。其中,一种方式如下:•另一种方式如下:•然后调用Open()函数打开连接。12.7.2ADO编程方法•Open()函数的原型如下:•Source:用于指明数据源,该参数是一个_variant_t类型的引用

。•ActiveConnection:表示一个连接对象的变量名或是一个包含连接信息的字符串。该参数也是一个_variant_t类型的引用。•CursorType:用于设臵游标类型。该参数的默认值为adOpenFowardOnly,该参数的其他取值如下表所示。12.7.2ADO编

程方法•LockType:用于设臵当打开记录集时的锁定类型。该参数的默认值为adLockReadOnly,该参数的其他取值如下表所示。12.7.2ADO编程方法•Options:用于设臵获取Source的方式。该参数的取值如下表所示。12.7.2ADO编程

方法•(5)获取记录集。获取记录集时,首先需要定义一个指向记录集对象的指针,然后为其创建一个实例对象。代码如下:•(6)对记录集进行操作。获取记录集后,可以对记录集进行操作。对记录集操作有两种方法:利用连接对象的Execute()方法执行

SQL语句和利用命令对象执行SQL语句。•利用连接对象执行SQL语句时,需要用到Execute()函数。该函数的原型如下:•CommandText:表示命令字符串,通常是SQL命令。•RecordsAffected:表示对记录集进行操

作后所影响的行数。•Options:表示CommandText中内容的类型。12.7.2ADO编程方法•执行简单操作时,一般采用连接对象执行SQL语句。但当执行比较复杂的命令时,一般采用命令对象执行SQL语句。•使用命令对象执行SQL语句时,首先需要创建命令对象,然后得用该对

象所拥有的函数进行操作。•(7)记录集的添加、修改和删除。当向记录集中添加记录时,首先需要调用AddNew()方法添加一个新的行,然后使用PutCollect()函数向新插入的行中添加对应字段的值,最后调用Recordset对象的Update()方法。•当

修改记录集中的记录时,同样要使用PutCollect()函数。•Recordset对象可支持两类更新:立即更新和批量更新。使用立即更新,一旦调用Update()方法,对数据的所有更改将被立即写入基本数据源。也可以使用Add

New()和Update()方法将值的数组作为参数传递,同时更新记录的若干字段。12.7.2ADO编程方法•如果提供者支持批量更新,可以使提供者将多个记录的更改存入缓存,然后使用UpdateBatch()方法在单个调用中将它们传送给数据库。这种情况应

用于使用AddNew()、Update()和DeleteSt()方法所做的更改。调用UpdateBatch()方法后,可以使用atus属性检查任何数据冲突并加以解决。•UpdateBatch()函数的原型如下:•HRESULTU

pdateBatch(enumAffectEnumAffectRecords);•AffectRecords:用于记录数据源中哪些数据被更新,默认值为adAffectAll。•如果想要取消当前的批量更新,可以使用Can

celBatch()函数。•recordset.CancelBatch(enumAffectEnumAffectRecords);•AffectRecords:表示CancelBatch()函数所影响的行数。12.7.2ADO编程方法•当删除记录集中的记录时,可以使

用Delete()函数。该函数的原型如下:•HRESULTDelete(enumAffectEnumAffectRecords);•AffectRecords:用于指定删除记录的行数。•(8)关闭记录集。对记录集进行一系列的操作完毕后,需要将记录集和数据库的连接关闭。•m_pRecordset

->Close();//关闭记录集•m_pConnection->Close();//关闭连接第13章多线程编程•Windows是一个多任务的操作系统。多线程运行可以提高系统的运行效率,因此使用比较广泛。本章将对进程与线程进行简单介绍。内容包括

:进程与线程、线程的使用、线程的终止与通信等。通过本章的学习,读者可以在程序中调用其他应用程序,可以实现进程间的通信,可以实现线程的同步。下面对多线程技术进行详细介绍。13.1进程与线程•进程(Process)是应用程序的执行

实例。每个进行都可以访问进程中的所有资源。一个进程是由一个或多个进行、代码、数据和应用程序在内存中的其他资源组成。•当一个应用程序启动,相应的一个进程进行也会启动,这个进行称为父进程。一个应用程序还可以启动其他进程,被启动的其他进程称为子进

程。想要查看进程,可以打开Windows的任务管理器。单击【进程】标签,在【进程】选择卡中可以查看当前系统中的各个进程,如右图所示。进程是资源的分配单位,每个进程都拥有自己的地址空间和上下文环境。13.1进程与线程•进程是线程的容器,线程是

进程内部的一个执行单元。一个进程可以有一个或是多个线程,但这些线程仅生存于该进程中。也就是说,线程是在它所属的进程地址空间里执行代码,并在进程的地址空间对数据进行操作。•线程用于描述进程中的运行路径。当一个进程被启动时,系统就要创建一个主线程。该主线程是应用程序需要的唯

一线程,但进程也可以创建其他线程来完善进程的其他操作。13.2线程的分类•在MFC中,线程分为两类:用户界面线程(User-InterfaceThread)和工作者线程(WorkerThread)。•用户界面线程:该线程通常用来处理用户的输入并

响应用户生成的事件和消息。用户界面线程是从CWinThread类派生而来的。在MFC中,CWinApp对象就是一个用户界面线程。通常情况下,用户界面线程都是主线程。当应用程序启动时,用户界面线程随之启动。当应

用程序退出时,用户界面线程也会随之终止。•工作者线程:该线程通常不需要用户进行输入,由后台进行处理。因此,工作者线程又被称为后台线程。工作者线程和用户界面线程的区别在于,工作者线程不用从CWinThread类派生。13.3线程类(CWinTh

read)•在MFC上,CWinThread类封装了对线程的操作。下面对CWinThtead类中的成员函数和成员变量作具体介绍。•其中,CWinThread类中常用的函数如下:•(1)调用CreateThread()函数可以创建一个新的线程,该函数的原型如下:•

dwCreateFlags:表示线程创建的标志。•nStackSize:表示线程堆栈的大小。•lpSecurityAttrs:表示线程的安全属性。13.3线程类(CWinThread)•(2)调用SetThreadPriority()函数

可以设臵线程的优先级,该函数的原型如下:•其中,nPriority参数定义了线程的优先级。其取值如下表所示。13.3线程类(CWinThread)•(3)调用GetThreadPriority()函数可以获取线程的优先级,该函数原型

如下:•(4)调用PostThreadMessage()函数可以向另一个CWinThread对象发送消息,该函数的原型如下:•message:表示用户定义消息的标识。•wParam:表示消息的第一个参数。•lParam:表示消息的第二个参数。13.3线程类(CWinThread)•(5)调用S

uspendThread()函数可以将线程的挂起计数加1,当线程的挂起计数大于0时,该线程将暂停执行,称之为挂起状态。该函数的原型如下:•(6)调用ResumeThread()函数可以将线程的挂起计数减1。当线程的挂起

计数减少到0时,恢复线程的执行。该函数的原型如下:13.3线程类(CWinThread)•在CWinThread类中,还需要重载一些函数来完成对线程的操作。•(1)重载InitInstance()函数

用于控制用户界面线程实例的初始化工作,该函数的原型如下:•(2)重载ExitInstance()函数用于控制清理操作,该函数的原型如下:•(3)重载OnIdle()函数用于控制线程空闲处理操作,该函数的原型如下:•l

Count:表示计数器。•此外,CWinThread类中有以下几个重要的成员变量。•m_hThread:表示当前线程的句柄。•m_nThreadID:表示当前线程的ID。•m_pMainWnd:表示指向

应用程序主窗口的指针。13.4线程的使用•在MFC中,创建一个新的线程时,可以使用全局函数AfxBeginThread()。AfxBeginThread()有两种原型,分别用于创建用户界面线程和工作者线程。13.4.1启用用户界面线程•当启用用户

界面线程时,调用AfxBeginThread()的原型如下:•pThreadClass:表示用户界面线程对象运程时类结构指针。•pParam:表示传递给控制函数的参数。•nPriority:表示线程的优先级。•nStackSize:

表示线程堆栈的大小。•dwCreateFlags:表示线程创建的标志。•lpSecurityAttrs:表示安全属性(SECURITY_ATTRIBUTES)结构指针。13.4.1启用用户界面线程•另外,启用用户界面线程还可以先创建一个CWinThread类的对象,然后调用Crea

teThread()函数。代码如下:13.4.2启用工作者线程•当启用工作者线程时,调用AfxBeginThread()的原型如下:•pfnThreadProc:表示工作者线程的控制函数指针。•pParam:表示传递给控制函数的参数。•nPriority:表示线程

的优先级。•nStackSize:表示线程堆栈的大小。•dwCreateFlags:表示线程创建的标志。•lpSecurityAttrs:表示安全属性(SECURITY_ATTRIBUTES)结构指针。•注意:函数的返回值是一个线程指针。一般情况下,需要保

存该指针,以便于以后根据该指针来终止该线程。13.4.3用户界面线程•在实际应用过程中,有时需要在线程中创建对话框。此时,可以使用用户界面线程来实现。在MFC中,创建用户界面线程的具体步骤如下:•(1)从CWinThread类派生一个子类。•(2)在子类的InitInstan

ce()函数中设臵线程的主窗口。•(3)调用AfxBeginThread()函数创建用户界面线程。13.4.3用户界面线程•【示例13-1】创建用户界面线程。在一个基于对话框的MFC应用程序中,创建一个用户界面线程用来启动另外一个对话框。•(1)创建一个基于对话框

的MFC应用程序sample1301。•(2)设计对话框资源,如下图所示。13.4.3用户界面线程•(3)单击【Insert】|【Resource】命令,弹出【InsertResource】(插入资源)对话框。选择Dialog结点,如左图所示。•(4)设计新插入的对话框资源,如右图所示。1

3.4.3用户界面线程•(5)双击新添加的对话框资源,为其添加相应的类CSubDlg,如左图所示。•(6)单击【Insert】|【NewClass】命令,弹出【NewClass】对话框。添加一个CWinThread类的子类CSubThread,如右图所示。13.4.3用户界面线程•(7)添

加并修改CSubThread类中的数据成员和成员函数。其中,SubThread.h文件中的代码如下:•……•SubThread.cpp文件中的代码如下:•……13.4.3用户界面线程•(8)在对话框类CSam

ple1301Dlg的【创建用户界面线程】按钮中添加如下代码:•在【取消】按钮中添加如下代码:13.4.3用户界面线程•(9)运行程序sample1301,弹出主对话框。单击【创建用户界面线程】按钮,弹出【用户界面线程】对话框。•当运行程序sample1301

的同时,可以打开系统的任务管理器进行查看。当主对话框弹出时,进程中多了一个sample1301.exe进程。当弹出【用户界面线程】对话框时,没有相应的进程出现。从上述内容中可以看出,本例成功地创建了一个

用户界面线程。13.4.4工作者线程•创建工作者线程不需要使用CWinThread类的派生类,而是需要实现控制函数。在工作者线程的控制函数中,定义了线程的生命周期。当进入控制函数时,线程将被启动。而当退出函数时,线程将被终止。•【示例13-2】创建工作者线程。在一个基于对话框的MFC应用程

序中,创建工作者线程,并在对话框进行初始化时启用。•(1)创建一个基于对话框的MFC应用程序sample1302。•(2)设计对话框资源,如下图所示。13.4.4工作者线程•(3)添加成员变量。为两个ListBox控件添加成员变量,分别为m_list1和m_list2。其中,类别

为Control,类型为CListBox。•(4)为CSample1302Dlg类添加两个自定义函数,分别如下:•(5)为控制函数添加代码。其中,FirstThread()函数中添加的代码如下:•……•SecondThrea

d()函数中添加的代码如下:•……13.4.4工作者线程•(6)在对话框类CSample1302Dlg的OnInitDialog()函数中添加如下代码:•(7)修改Sample1302Dlg.h文件中的函数声明,将线程函数声明

为static函数。13.4.4工作者线程•(8)运行程序sample1302,结果如图13.8所示。•注意:如果不按照第(7)步骤对Sample1302Dlg.h文件进行修改,会出现如下错误提示:•'AfxBeginThread':n

oneofthe2overloadscanconvertparameter1fromtype'unsignedint(void*)'。13.5线程的终止•当一个线程终止时,关闭该线程所属的所有对象句柄

,将该线程对象状态变为有信息号状态,并将该线程对象终止状态STILL_ACTIVE改为退出码。通常情况下,线程终止分为两种:正常终止和异常终止。13.5.1正常终止线程•对于用户界面线程而言,使其正常终止只需在用户界面线程中调用PostQu

itMessage()即可。该函数的原型如下:•nExitCode:表示线程的退出码。•对于工作者线程而言,当控制函数正常执行到函数的结束点(return语句)后,该线程也就正常终止了。另外,用户也可以选择使用AfxEndThread()函数来终止该线程。AfxEndThread()函

数的原型如下:13.5.2异常终止线程•异常终止是指线程内部出现自身无法终止的情况,在其他线程中强行终止该线程。异常终止用于在紧急情况下安全退出控制。如果想要终止线程,可以使用TerminateThread()函数。该函数的原型如下:•hThread:表示将要终止的线程句柄。该参数可

以使用创建线程返回时返回的CWinThread从m_hThread成员变量中得到。•dwExitCode:表示线程的退出码。13.5.3线程的退出码•根据线程的退出码,可以判断线程的执行情况。使用GetExitCod

e()函数可以获取工作者线程或用户界面线程退出码。该函数的原型如下:•hThread:表示线程句柄。该参数可以使用创建线程返回时返回的CWinThread从m_hThread成员变量中得到。•lpEx

itCode:表示接收返回的终止状态的地址。13.5.3线程的退出码•如果线程没有被终止,则终止状态返回STILL_ACTIVE。如果线程已经被终止,则返回的终止状态可以是以下取值中的一个:•在ExitThread()

或TerminateThread()函数中指定的退出值。•在线程函数的return语句中的返回值。•线程所属进程中的退出值。13.5.3线程的退出码•想要获取线程的退出码,还要做一些其他工作。一般线程终止后,线程对象将被删除,用户不能获取m_hThread句柄。想要解决

这个问题,可以采用以下两个办法。•(1)在创建线程后,设臵线程对象的m_bAutoDelete成员变量为FALSE。这样,CWinThread对象在线程终止后仍将存在。在退出应用程序之前,还需要手动添加删除CWinThread线程对象的代码。•(2)单独选择保存线程句柄。在创建线程后,使用W

in32函数DuplicateHandle()将m_hThread复制到另一个句柄。这样,尽管对象被自动删除了,但使用复制的句仍然可以获取线程的退出码。•注意:采用第二种方法时,在复制句柄之前,线程不能终止。最

安全的方法是在创建线程进使用CREATE_SUSPENDED挂起,复制好后,调用ResumeThread()函数恢复线程的执行。13.6线程的通信•通常情况下,在次线程是为主线程服务的,这就预示着主线程和次线程之间会进行及时的通信。下面对线程间的通信作简单介绍。

13.6.1通信原理•在线程间进行通信的过程中,需要用到以下几个函数。其中,调用PostMessage()函数可以进行线程间的通信。该函数的原型如下:•hWnd:表示消息发送的目的窗口句柄。•Msg:表示将要发送的消息。•wParam:表示消息的第1个参数。

•lParam:表示消息的第2个参数。13.6.1通信原理•调用PostThreadMessage()函数可以向某个线程发送消息,该函数的原型如下:•idThread:表示线程标识。•Msg:表示将要发送的消息。•wParam:表示消息的第1个参数。•lParam:表示消息的第2个参数。13.6.

1通信原理•如果想要对消息进行外理,用户界面线程和工作者线程的处理方式不同。•在用户界面线程中,有两种方式对消息进行处理。•(1)使用消息映射宏ON_THREAD_MESSAGE。该宏的定义格式如下:•message:表示消息标识ID。•memberFxn:表示CWinT

hread消息处理函数的名称。•(2)使用PreTranslateMessage()函数,直接对消息进行处理。该函数的原型如下:•pMeg:表示包含消息的MSG结构指针。13.6.1通信原理•在工作者线程中,可以使用GetMessage()函数对消

息进行处理。该函数的原型如下:•lpMsg:表示消息MSG结构指针。•hWnd:表示窗口句柄。•wMsgFilterMin:表示第1个消息。•wMsgFilterMax:表示最后一个消息。13.6.2用户界面线程通信•下面通过一个实例来介绍用户界面线程间的通信。•【示例13-3】实现用户界

面线程间的通信。•(1)创建一个基于对话框的MFC应用程序sample1303。•(2)设计对话框资源,如下图所示。•(3)添加成员变量。其中,从上到下,各文本框对应的成员变量分别为m_edit1、m_edit2和m_edit3。其中,m_edit1、m_

edit2的类别为Value,类型为int。m_edit3的类别为Control,类型为CEdit。13.6.2用户界面线程通信•(4)添加CWinThread类的派生类。单击【Insert】|【NewClass】命令,弹出【NewClass】对话框。添

加一个CWinThread类的子类CWinThread,如图13.10所示。13.6.2用户界面线程通信•(5)在CompareThread.h文件中添加如下代码:•在CompareThread.cpp文件中添加如下代码:•(6)在

CSample1303Dlg类中添加一条自定义消息,代码如下:13.6.2用户界面线程通信•(7)重载CCompareThread类的PreTranslateMessage()函数。右击CCompareThread类,在弹出的弹出式菜

单中选择【AddVirtualFunction】菜单项,弹出【NewVirtualOverrideforClassCCompareThread】对话框。从左边的列表框中双击【PreTranslateMessage】选项,将其移动

到右边的列表框中,如下图所示。13.6.2用户界面线程通信•(8)单击【AddandEdit】按钮,进入编辑区。在PreTranslateMessage()函数中添加代码如下:•……13.6.2用户界面线程通信•(9)为对话框类CSample1303Dlg添加一

个成员变量,如下图所示。该变量用于保存线程的标识。•(10)在对话框类CSample1303Dlg的初始化函数中创建并启动用户界面线程,代码如下:13.6.2用户界面线程通信•(11)在【比较】按钮的消息响应函数中添加如下代码:•(12)运行程序sample1303,结果如下图所示。•在运行程序时

,单击【比较】按钮,并没有直接对a、b的值进行比较,而是发送一条WM_CPMPARE的消息,交由CCompareThread类的PreTranslateMessage()函数进行处理。13.7线程的同步•在编写多线程应

用程序时,经常会出现线程间同步访问共享资源的情况。多个线程同时访问同一个共享资源将可能发生无法预知的错误。例如,一个线程正在更新数据集,而同时另外一个线程正在读取数据。那么,第二个线程只会读取一部分正确的数据。•为了解决这

个问题,VisualC++6.0提供了同步类。同步类包括同步对象和同步访问对象。其中,同步对象分为4种,分别是CSemaphore(信号量)、CCriticalSection(临界区)、CMutex(互斥量)和CEvent(事件)。下面对这些同步类分别进行介绍。13.7

.1同步访问对象•同步访问对象提供了对同步对象的封装。同步访问对象分为两种,分别是CSingleLock和CMultiLock。•1.CSingleLock•如果一次只需要等待一个同步对象,可以使用C

SingleLock类的对象。CSingleLock类的构造函数如下:•pObject:表示同步对象指针。该参数不能为NULL。•bInitialLock:表示是否在初始化时对同步对象进行访问。•如果想要获取同步对象,可以使用Lock

()函数。该函数的原型如下:•dwTimeOut:表示同步对象等待的时间。13.7.1同步访问对象•如果想要释放同步对象,可以使用Unlock()函数。该函数的原型如下:•lCount:表示要释放的对象数。•lPrevCount:表示同步对象接收的前一次的个

数。•如果想要判断等待的同步对象是否被锁定,可以使用IsLocked()函数。该函数的原型如下:13.7.1同步访问对象•2.CMultiLock•如果在某个特定的时刻要使用多个同步对象,可以使用CMultiLock类的对象。CMultiLock类的构造函数原型如

下:•ppObjects:表示同步对象数组指针。•dwCount:表示同步对象数组元素的个数。•bInitialLock:表示是否在初始化时对同步对象进行访问。13.7.1同步访问对象•如果想要获取同

步对象,可以使用Lock()函数。该函数的原型如下:•dwTimeOut:表示同步对象等待的时间。•bWaitForAll:表示是否等待所有的同步对象。•dwWakeMask:指定其他放弃等待的条件。

13.7.1同步访问对象•如果想要释放同步对象,可以使用Unlock()函数。该函数的原型如下:•lCount:表示要师范的对象数,该参数必须大于0。•lPrevCount:表示同步对象接收的前一次的个数。•如果想要判断等待的

同步对象是否被锁定,可以使用IsLocked()函数。该函数的原型如下:13.7.2使用信号量实现线程同步•CSemaphore类对象代表一个“信号量”,可以允许一定数目的线程访问某个共享资源。信号量对象中保存了

当前同时访问某个共享资源的线程计数。如果该计数值为0,则所有对这个CSemaphore类对象所控制资源的访问将被放到一个队列中进行等待,直到超时或计数值不为0。13.7.2使用信号量实现线程同步•CSemaphore类的构造函数原型如下:•lInitialCount:

表示信号量对象初始化计数。该参数的值决定了在信号量对象建成后,能同时访问其中资源的最大线程数目。该参数的取值在0~IMaxCount之间。•lMaxCount:表示信号量对象的最大计数。该参数必须大于0。•pstrName:表示要创建的信号量对象的

名称。•lpsaAttributes:表示信号量对象的安全属性的指针。该参数一般设臵为NULL。13.7.3使用临界区对象实现线程同步•CCriticalSection类对象代表一个“临界区”。当多个线程访问一个独占性共享资源时,可以使用临界区对象。在同一时刻,只允许一个线程可以拥有临界区对

象,进而访问资源或是代码段,而其他线程需要等待。•CCriticalSection类的构造函数原型如下:13.7.4使用互斥量对象实现线程同步•CMutex类对象代表一个“互斥量”。互斥量对象属于系统内核对象,它能够使线程拥有对某个资源的绝对访问权限。CMute

x类的构造函数原型如下:•bInitiallyOwn:表示互斥量对象的初始状态。•lpszName:表示互斥量对象的名称。•lpsaAttribute:表示互斥量对象的安全属性。13.7.5使用事件对象实现线程同步•CEvent类对象代表一个“事件”。事件对象是最基本的内核对象,

在处理线程同步时,其使用的最为广泛。事件对象主要分为两种:人工重臵事件对象和自动重臵事件对象。对于人工重臵事件对象,可以同时拥有多个线程等待到事件对象,成为可调度线程。对于自动重臵事件对象,等待该事件对象的多个线程只能有一个线程成为可调度线程。CEvent类的构造函数

原型如下:•bInitiallyOwn:表示初始化时是否允许同步访问对象拥有该对象。•bManualReset:指定事件对象类型,即人工重臵事件对象和自动重臵事件对象。•lpszName:表示事件对象的名称。•lpsaAttribu

te:表示事件对象的安全属性。13.7.5使用事件对象实现线程同步•Windows系统提供了CreateEvent()函数用于创建事件对象。该函数的原型如下:•lpEventAttributes:表示事件对象的安全属性。•bManualReset:指定事件对象类型,即人工

重臵事件对象和自动重臵事件对象。•bInitialState:表示事件对象的初始状态。•lpName:表示事件对象的名称。13.7.5使用事件对象实现线程同步•在事件对象创建后,可以使用SetEvent()函

数设臵事件对象为可用,并释放所有的等待线程。该函数的原型如下:•hEvent:表示事件对象句柄。•使用ResetEvent()函数设臵事件对象为无信号状态。该函数的原型如下:•hEvent:表示事件对象句柄

。第14章WinSock网络通信开发•Windows应用程序可以有无限的网络功能,都是建立在WinSock接口的基础上。WinSock是WindowsSockets的简称,也称为Windows套接字,是微软根据BSDUNIX操作系统中流行的Berkele

y套接字规范而实现的一套MicosoftWindows下的网络编程接口。•本章将具体介绍在VC中,基于Winsock接口进行网络通信程序的开发的基础知识。14.1网络通信与WinSock基础•网络通信程序是指应用程序需要与网络中其他系统上的应用程序

之间进行通讯。在介绍网络通信程序的开发之前,首先简单介绍一下网络通信和WinSock的基础知识和基本概念。14.1.1WinSock的基本概念•Windows下网络编程的规范——WindowsSockets(简

称WinSock)是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。它经过不断完善,在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。•WindowsSockets规范意

图在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。任何能够与WinSock兼容实现协同工作的应用程序就被认为是具有WinSock接口。称这种应用程序为WinSock应用程序。WinSock规范定义并记录了如何使用API与Internet协议族(IPS,通常指的是TCP/I

P)连接,尤其要指出的是所有的WinSock实现都支持流套接字和数据报套接字。14.1.1WinSock的基本概念•应用程序则通过调用WinSock提供的API实现相互之间的通讯,而WinSock又利用下层的网络通讯协议功

能和操作系统调用实现实际的通讯工作。•在ISO的OSI网络七层协议中,WinSock主要负责的是控制数据的输入和输出,也就是传输层和网络层。它屏蔽了数据链路层和物理层,给Windows下的网络编程带来了巨大的变化。14.1

.2TCP/IP协议与WinSock•Internet是建立在TCP/IP协议基础之上,采用了TCP/IP的网络体系结构。TCP/IP不是一个简单的协议,而是一组小的、专业化协议,包括TCP、IP、UDP、ARP、ICMP以及其它

的一些被称为子协议的协议。大部分网络管理员将整组协议称为TCP/IP,有时简称为IP。其中的几个重要协议介绍如下:•TCP(TransmissionControlProtocol,传送控制协议):这是一种提供给用户进程的可靠的全双工字节流面向连接的协议。它

要为用户进程提供虚电路服务,并为数据可靠传输建立检查。大多数网络用户程序使用TCP。•UDP(UserDatagramProtocol,用户数据报协议):这是提供给用户进程的无连接协议,用于传送数据而

不执行正确性检查。•IP(InternetProtocol,网间协议):负责主机间数据的路由和网络上数据的存储,同时为ICMP,TCP,UDP提供分组发送服务,用户进程通常不需要涉及这一层。•TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通

常是在操作系统内核中实现,因此用户一般不涉及。14.1.2TCP/IP协议与WinSock•编程时,编程界面有两种形式:一是由内核心直接提供的系统调用;二是使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。用户服务要通过核外的应

用程序才能实现,所以要使用套接字(WinSock)来实现。TCP/IP协议核心与应用程序关系如下图所示。14.1.3WinSock通信与C/S结构•WindowsSockets通信的基础是套接字(Socket)。与文件操作类似,当要读写一个文件

时,必须用一个文件对象(文件指针或文件句柄)执行这个文件。而Socket就是在应用程序之间用来读(接收信息)或写(发送信息)的一个网络对象。•WindowsSockets支持两种类型的套接字:流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)。•流式套接字定义了

一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。•数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错。14.1.3WinSock通信与C/S结构•对于要求精确传输数据的WindowsSockets通信程序

,一般采用流式套接字。采用流式套接字通信的一个最典型的应用就是客户机/服务器(C/S)模型,这也是在TCP/IP网络中,两个进程间的相互作用的主要模式。客户机/服务器模式在操作过程中采取的是主动请示方式,其具体工作流程如下图所示。14.1.3Wi

nSock通信与C/S结构•首先服务器方要先启动,并根据请示提供相应服务,具体过程如下:•(1)打开一通信通道(Socket)并告知本地主机,它准备在某一个地址上接收客户的连接请求。•(2)进入监听状态,等待客户请求到达该端口。•(3)接收到客户服务请

求,处理该请求并发送应答信号。•(4)返回第二步,等待另一客户请求。•(5)关闭服务器。14.1.3WinSock通信与C/S结构•而客户方的工作过程如下:•(1)打开一通信通道(Socket),并连接到服务器所在主机的特定端口。•

(2)向服务器发送服务请求报文,等待并接收应答,继续提出请求……。•(3)请求结束后关闭通信通道并终止。14.1.4MFC中WinSock的封装类•当然,在VC6.0中,程序员可以直接使用WindowsSocketsAPI

函数进行WinSock网络程序的开发。•WindowsSocketsAPI是以BerkeleySocketsAPI为模型,提供了一个标准的API,Windows程序员可以使用它来编写网络应用程序。WindowsSocketsA

PI函数共包括三大类:套接字函数、数据库函数和针对MicrosoftWindows的扩展函数。其具体使用本书不作详细介绍。•MFC为套接字提供了封装类CAsyncSocket和CSocket,它们封装了WindowsSockets的API,从而程序员可以

用面向对象的方法调用Socket。14.1.4MFC中WinSock的封装类•CAsyncSocket类是在一个较低的层次上封装了WindowsSocketsAPI,它封装了异步、非阻塞Socket的

基本功能,提供了Socket的基本操作,用它做常用的网络通信软件很方便。CSocket类由CAsyncSocket派生,是WindowsSocketsAPI的高层抽象,提供了更高层次的功能——阻塞式的访问方式。有关函数的原型及使用,在下面将结合具体的实例开发来介绍。CSo

cket类新提供的主要成员函数及其功能如下表所示。14.1.5WinSock网络编程的常用术语•对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手。许多概念,诸如同步(Sync)、异步(Async)、阻塞(Block)、非

阻塞(Unblock)等,初学者往往迷惑不清,这里对一些常用术语作一下简单介绍。1、套接字•套接字也就是前面多次提到的Socket,是可以被命名和寻址的通信端点,是网络的基本构件。实际上,套接字可以理解为在计算机中提供的一个通信端口,可以通过这个端口与任何一个具有Socket接口的

计算机通信。应用程序在网络上传输、接收的信息都通过这个Socket接口来实现的。•使用中的每一个套接字都有其类型和一个与之相连的进程。WINDOWSSOCKET1.1版本支持两种类型的套接字:流套接字(SOCK_ST

REAM)和数据报套接字(SOCK_DGRAM)。2、同步/异步•同步方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式;而异步指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。3、

阻塞模式/非阻塞模式•CSocket类创建的套接字支持阻塞模式,阻塞模式简单来说就是服务端与客户端之间的通信处于同步状态下。所谓阻塞套接字是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上。比如调用Receive函数读取网络缓冲区中的数据,如果没有数据到达,程序将一

直停止在Receive这个函数调用上,直到读到一些数据,此函数调用才返回。•在非阻塞模式下利用Socket事件的消息机制,服务端与客户端之间的通信处于异步状态下。即执行非阻塞套接字的网络调用时,不管是否执行成功,都立即返回。比如调用Receive函数读取网络缓冲区中数据,不管是否读到数据都立即

返回,而不会一直停止在此函数调用上。14.2无连接通信开发•使用Socket进行通信,有两种主要的方式:面向连接的流方式和无连接的数据报文方式。进行无连接通信时,通信的两台计算机像是把数据放在一个信封里,通过网络寄

给对方,信在传送的过程种有可能会残缺,也有可能后发的先到。该种方式支持双向的数据流,但并不保证是可靠、有序、无重复的。使用Socket进行无连接通信开发时,需要创建数据报套接字(SOCK_DGRAM)。14.2.1Socket无连接通信机制•在IP中,无连接通信是通过UDP/IP协议完

成的。UDP不能确保可靠的数据传输,但能将数据发送到多个目标,或者接收多个源数据。如果一个客户端向服务器发送数据,则该数据会立刻被传输,不管服务器是否准备接收这些数据。如果服务器接收到客户端发送的数据,也不需要向客户端发送消息以确认数据被收到。UDP/IP的数据传输使用数据报,即

离散的信息包。•使用无连接通信时,服务器端和客户端之间差别不大,通信双方处于完全对等状态,不存在监听与连接过程。这里就分别以发送端和接收端来区分双方,通过CSocket类实现Socket,简单介绍一下无连接通信的通信流程。1.接收端•对于在一个无连接的套接字上接收数据来说,

并不是很复杂。具体流程可表示如下:•(1)初始化WinSock的动态连接库后,首先构造CSocket套接字对象,使用Creat函数创建数据报格式的套接字。•(2)通过Bind函数在将该套接字与准备接收数据的接口绑定在一起。•(3)使用Receive函数接收对方

发送的数据,此时程序处于阻塞状态,直到接收到数据自动返回。•(4)接收数据完毕,使用Close函数关闭套接字。2.发送端•在一个无连接的套接字上发送数据的具体流程可表示如下:•(1)首先构造CSocket套接字对象,使用Creat函数创建数据报格式的

套接字。•(2)通过Bind函数在将该套接字与准备发送数据的接口绑定在一起。•(3)使用Send函数向对方发送数据。•(4)发送完毕,使用Close函数关闭套接字。14.2.2主要功能函数介绍•前面已经介绍过,在MFC中,CAsyncSocket和CSo

cket类封装了WindowsSocketsAPI函数。本节将结合套接字的创建和使用过程,介绍无连接通信中,使用的CAsyncSocket类的主要成员函数(CSocket类由CAsyncSocket类派生)。1.WinSock环境的初始化•在使用W

inSockMFC类之前,必须为应用程序初始化WinSock环境。其实现只要调用全局函数AfxSocketInit即可。如下面的代码:•同时,在“stdafx.h‖文件中添加如下代码:•如果在使用MFCAppWizard[EXE创建MFC工程时,在MFCAppWizardStep

2对话框选择“WindowsSockets‖选项,则程序回自动添加上面的代码,实现WinSock的初始化。2.创建Socket•创建Socket,首先需要构造Socket对象,而后调用Create函数创建

Socket。Create函数原型如下:•各参数含义如下:•nSocketPort:为使用的端口号,默认为0,表示由系统自动选择,通常在客户端都使用这个选择。•nSocketType:为使用的协议族,默认为SOCK_STREAM,表示使用面向连接的流服务;为SOCK_DGRA

M,表示使用无连接的数据报服务。•lpszSocketAddress:为本地的IP地址,可以使用点分法表示如“127.0.0.1‖。2.创建Socket•也可以通过使用Bind函数设臵Socket的地址和端口号,如下:•表

示该Socket对象的地址为“168.0.1‖,端口为4800。•通过Socket提供的send()和Receive()函数可以实现任何类型数据的发送和接收。3.发送、接收数据•通过Socket连接发送和接收数据比较简单。可以用Sock

et发送任何类型的数据,只需要一个指向存放数据的缓冲区指针即可。发送时,缓冲区存放待发送的数据;接收时,接收的数据将拷贝到缓冲区。•(1)发送数据•可以使用Send函数通过Socket连接发送数据,函数的原型如下:•各参数含义如下:•lpBuf:指向发送数据缓冲区

的指针,如果数据为CString变量,可使用LPCTSTR操作符把CString变量作为缓冲区传送。•nBufLen:指明缓冲区要发送数据的长度。•nFlags:该参数是可选的,用于控制消息的发送方式。•函数执行成功,返回发送到对方应用程序的数据总量。如果有错误产生,函

数返回SOCKET_ERROR。3.发送、接收数据•(2)接收数据•Socket接收数据时,就需要调用Receive函数。Receive函数原型如下:•Receive函数的参数与Send函数基本相同,lpBuf为缓冲区指针,指明接收数据存储的位臵,

参数nBufLen是缓冲区的长度,指示Socket能存储多少数据。nFlags为标记位,收发双方需要指明相同的标记。•执行成功后,Receive函数也返回接收到的数据的数据量。如果有错误产生,函数返回SOCKET_ERROR。3.发送

、接收数据•有一点需要说明,在接收数据时,最后一个字符后面最好设臵一个NULL字。因为缓冲区中可能会有一些垃圾数据,如果接收的数据后面不加NULL,应用程序可能会把这些垃圾数据作为接收数据的一部分。如下面的实现代码:•对于无连接通信,即数据报类型的Socket,发送和接收数据还可以使用Sen

dTo和ReceiveFrom函数,其功能和使用与Send和Receive函数基本相同。4.关闭Socket连接•当应用程序完成与对端的所有通信之后,就可以调用Close函数关闭这次连接。Close函数不带任何参数,调用方式如下:•有时可能需要在Socke

t关闭之前就让其停止运行,这时可调用Shutdown函数。Shutdown函数原型如下:4.关闭Socket连接•该函数只需要一个整形参数nHow,指明是否关闭此Socket数据的发送或接收,如下表所示。

•需要注意的是,调用Shutdown函数并不关闭网络连接,也不能释放Socket所占用的任何资源,程序完成后,仍然需要调用Close函数关闭Socket。14.2.3无连接通信接收端的实现•无连接通信发送和接收方

处于相同的地位没有主次之分。常用于对数据可靠性要求不高的通信,如实时的语音、图像传送和广播消息等。14.2.4无连接通信发送端的实现•要在一个无连接的套接字上发送数据,最简单的方法就是创建一个套接字,然后调用SendTo函数向指定接口发送数据即可。14.3面

向连接通信开发•与无连接通信不同,面向连接通信要求两个通信的应用程序之间首先要建立一条连接链路,而后数据才能被正确接收和发送。面向连接通信的特点是通信可靠,对数据有重发和校验机制,通常用来做数据文件的传输如FTP,Telnet等。使用Socket进行面向连接通信开发时,需要创建流

式套接字(SOCK_STREAM)。14.3.1Socket面向连接通信机制•在IP中,面向连接地通信是通过TCP/IP协议完成地,TCP提供两个计算机间可靠无误地数据传输。应用程序使用TCP通信时,在源计算

机和目标计算机之间,会建立起一个虚拟连接。建立连接之后,计算机之间便能以双向字节流的方式进行数据交换。•与无连接通信不同,面向连接通信,必需有一方扮演服务器的角色,等待另一方(客户端)的连接请求。服务器方需要首先创建一个监听套接字,在此套接字上等待连接。当连接建立后会产生

一个新的套接字用于与客户端通信。以CSocket类实现Socket通信为例,简单介绍一下面向连接通信的通信流程。1.服务器端•面向连接通信服务器端的具体实现流程可表示如下:•(1)创建监听Socket

对象。初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,即使首先构造一个CSocket对象,而后通过调用Create函数创建一个流套接字。•(2)绑定监听Socket的端口。使用Bind函数为服务器端定义的这个监听的Socket指定一个地址及端口,这样客

户端才知道要连接哪一个地址的哪个端口。•(3)进入监听状态。使用Listen函数使服务器端的Socket进入监听状态,并设定可以建立的最大连接数。•(4)接受用户的连接请求。服务器进入到监听模式后,便已经做好了可以接受客户端连接的准备了。下面就可以通过Accept函数来接受用户的

连接请求。•(5)与客户端进行通信。Accept函数执行后,将新建一个通信Socket与客户端的Socket相通,原先的监听Socket继续进入监听状态,等待他人的连接要求。通信Socket就可以通过Read和Write函数与客户端进行通信。•(6)

关闭服务。当要关闭服务器时,使用Close函数关闭监听套接字和通信套接字即可。2.客户端•面向连接通信客户端网络连接的创建相对服务器端要简单,其具体实现流程可表示如下:•(1)创建客户端Socket。初始

化WinSock的动态连接库后,首先构造CSocket套接字对象,使用Creat函数创建套接字。与服务器端的Socket不同的是,客户端的Socket可以调用Bind函数,由自己来指定IP地址及端口号,也可以不调用Bind,而由Winsock来自动设定IP地址和端口。•(2)提出连接请

求。客户端的Socket使用Connect函数来提出与服务器端的Socket建立连接的申请。•(3)与服务器通信。服务器端接受客户Socket的连接请求后,便可以通过Read和Write函数与服务器端进

行通信。•(4)断开连接。使用Close函数关闭客户端Socket即可实现断开与服务器的连接。14.3.2主要功能函数介绍•在上节介绍了面向连接通信程序开发的基本流程和使用的Socket类相关的成员函数。其中大部分函数在前面已

有详细介绍,本节将详细介绍一下面向连接通信特有的有关连接的几个函数的使用。1.监听函数Listen•服务器端的监听Socke通过调用Listen成员函数,使Socket处于监听状态,监听对方(客户端)的连接请求。Listen函数原型如下:•参数nConnect

ionBacklog为Socket同时可以接受的连接数,默认值为5,也是最大值。函数成功执行则返回一个非0值。•Listen函数的典型调用代码如下:2.连接函数Connect•客户端Socket通过调用Conne

ct函数连接服务器,其原型如下:•两个参数分别为要连接的计算机(服务器)的IP地址和端口号。•Connect函数的典型调用代码如下:•当执行CSocket类的Connect函数时,在连接成功之前不会返回,即程序处于阻塞状态,只有成功连接或者出了故障不能进行

连接才返回。3.接受连接函数Acceptt•服务端Socke通过调用Accept函数接受客户端连接请求。Accept函数的原型如下:•其中,参数rConnectedSocket为一个新的套接字,用于与连接方通信;参数lpSockAddr为SOCKADDR结

构的指针,用于记录连接方(客户端)的IP地址信息;参数lpSockAddrLen则存储lpSockAddr信息的长度。3.接受连接函数Acceptt•当连接建立后,一个新的套接字将被创建,该套接字用于与对方应用程序连接。•Accept函数的典型应用代码如下

:•当使用CSocket类时,Accept函数直到收到连接请求并接受后,函数才返回。14.3.3面向连接通信服务器端的实现•与无连接通信的实例相对应,本节将给出一个面向连接通信服务器端的实例。实例将实现在固定端口创建监听套接字,等待客户端的连接。当有客户端连接时,通

过新建的通信套接字向客户套接字定时(间隔1.5秒)发送数据,并显示当前的发送状态和发送的数据量。14.4Socket非阻塞模式及开发•当有多个客户端Socket与服务端Socket连接及通信时,服务端采用阻塞模式就显得不适合了,应该采用非阻塞模式,利用Socket事件的消息机制来接受多个客户端

Socket的连接请求并进行通信。本节将重点介绍Socket事件的处理机制,并给出一个网络聊天室的开发实例。14.4.1CSocket阻塞模式•在前面介绍的的面向连接通信服务器端程序开发中,监听套接字采用的是CSocket的阻塞模式。很显然,如果没有来自客户端Socket

的连接请求,Socket就会不断调用Accept函数产生循环阻塞,直到有来自客户端Socket的连接请求而解除阻塞。阻塞解除后,表示服务端Socket和客户端Socket已成功连接,服务端与客户端彼此可相互调用Send和Rece

ive方法开始通信。14.4.1CSocket阻塞模式•如果服务端用于监听的Socket在主线程中运行,这将导致主线程的阻塞。因此,当有多个客户连接服务器时,需要分别为其创建一个新的线程,以运行Socket服务,如下图所示。显然,采用这种方式增加了程序

设计的复杂性。14.4.2CSocket非阻塞模式——事件处理•非阻塞模式是指服务端与客户端之间的通信处于异步状态下,可以通过Socket事件的消息机制来实现。•使用CSocket(或者CAsyncSo

cket)类创建的客户Socket与服务器Socket进行连接通信时,会触发许多事件。如当客户端Socket成功连接服务器时,会触发FD_CONNECT事件,而服务器端接受用户连接,会触发FD_ACCEPT事件等等。为了响应这些事件,

在CAsyncSocket类中,定义了一系列可重载的函数,如OnConnect、OnAccept等。14.4.2CSocket非阻塞模式——事件处理•各事件及其对应的响应函数如下表所示。14.4.2CSocket非阻塞模式——

事件处理•这样,用户就可以从CSocket类(或者CAsyncSocket)派生一个新类,通过重载这些Socket事件的响应函数,实现非阻塞模式的面向连接通信。简单来讲,如前面介绍的面向连接的服务器端的例程,采用的是CSocket的阻塞模式,其启动按钮响应

函数如下:14.4.2CSocket非阻塞模式——事件处理•Accept函数阻塞了主线程,等待用户的连接。而如果这里采用Socket事件机制,便可以实现非阻塞方式。简单来说,就是从CSocket类派生新的监听Socket类CListenSocket,在其中重载OnAccept函数

,在OnAccept函数中实现创建客户套接字与客户端进行通信。此时,启动按钮响应函数可表示如下:

小橙橙
小橙橙
文档分享,欢迎浏览!
  • 文档 25747
  • 被下载 7
  • 被收藏 0
相关资源
广告代码123
若发现您的权益受到侵害,请立即联系客服,我们会尽快为您处理。侵权客服QQ:395972555 (支持时间:9:00-21:00) 公众号
Powered by 太赞文库
×
确认删除?