Visual C++基本原理有哪些?
Visual C++基本原理有哪些?
Visual C++ 基本原理
// Name: Easyright
// Date: 8-1-2003
// Homepage: http://www.easyright.net
// Email: support@easyright.net
FAQ
问:阅读以下文章需要具备哪些知识?
答:只要会开机就行了,如果大家有C++和面向对象(Object-Oriented)的基础知识,会有事半功倍的效果。
问:必须具备哪些软件?
答:Windows 98, Windows NT, Windows 2000中的任意一种,另外再加上Visual C++ 5.0或6.0。由于我没有测试过Windows 95和Visual C++ 5.0以下的版本,所以不知道他们可不可用。
问:为什么用Visual C++?
答:因为VC功能强而多。
基本概念
心情随笔:其实这一章是最枯燥的,概念又多,本来我不想写这一章的,但为了照顾初学者,我觉得还是有必要讲一 下。由于这一章是属于"入门篇"的,所以大家只需要了解以下内容就行了,不需要深入研究其原理,到了"高级篇"时,我们还会重新仔细分析其原理的,希望大 家不会被这一章的内容吓跑,有问题就去本站的留言本留言吧。
首先我们要了解以下概念:
应用程序(Application),他就是由指令(Instruction)组成的可以运行的文件。
进程(Process),有时和应用程序的意思一样,但在通常的情况下,进程是指一个正在运行的应用程序,正因为这样,进程由以下部分组成:
1、一个可以执行的程序
2、位于内存(Memory)中的私有地址空间
3、系统资源(System Resource),例如文件(File), 管道(Pipe), 通讯端口(Communications Port), 信号(Semaphore)
4、至少还要有1个线程(Thread), 线程是最基本的执行单位。
因为多个进程是可以同时存在时,所以Windows操作系统(Operating System)必须给进程提供保护,以防止他们冲突。
物理内存(Physical Memory),即你的计算机的实际内存,例如我现在用的电脑的内存是128M,物理内存的容量是达不到程序的要求的,于是就产生了虚拟内存(Virtual Memory)。
虚拟内存(Virtual Memory), 不是真正的内存,它通过映射(Map)的方法,使可用的虚拟地址(Virtual Address)达到4G(2的32次方),每个应用程序可以被分配2G的虚拟地址,剩下的2G留给操作系统自己用。在Windows NT中,应用程序可以有3G的虚拟地址。简单的说,虚拟内存的实现方法和过程是:
1、当一个应用程序被启动时,操作系统就创建一个新进程, 并给每个进程分配了2G的虚拟地址(不是内存,只是地址);
2、虚拟内存管理器(Virtual Memory Manager)将应用程序的代码(Code)映射到那个应用程序的虚拟地址中的某个位置,并把当前所需要的代码读取到物理地址中。注意,虚拟地址和应用程序代码在物理内存中的位置是没有关的;
3、如果你有使用动态链接库(Dynamic-Link Library,即DLL)的话,DLL也被映射到进程的虚拟地址空间,在有需要的时候才被读入物理内存;
4、其他项目(例如数据,堆栈等)的空间是从物理内存分配的,并被映射到虚拟地址空间中;
5、应用程序通过使用它的虚拟地址空间中的地址开始执行,然后虚拟内存管理器把每次的内存访问映射到物理位置。
如果大家看不明白上面的步骤也不要紧(似乎超出了入门篇的范围),但大家要明白以下两点:
1、应用程序是不会直接访问物理地址的;
2、虚拟内存管理器通过虚拟地址的访问请求,控制所有的物理地址访问;
使用虚拟内存的好处是:简化了内存的管理,并可以弥补物理内存的不足;可以防止在多任务(Multitasking)环境下的各个应用程序之间的冲突。
线程(Thread),是最基本的执行单位,CPU时间就是分配给每个线程的。每个进程一开始时只有一个线 程,但每个线程都可以产生出其他线程,前者叫做父线程(Parent Thread),后者叫做子线程(Child Thread)。每个执行的线程都有自己的虚拟输入队列(Virtual Input Queue),用来处理来自硬件、处理器(Processor)或操作系统的消息(Message)。这些队列都是异步的,也就是说,当处理器发送一个消 息给另外一个线程的队列时,发送函数不用等待其他线程处理该消息就可返回,而接收消息的线程可以等到该线程准备好时再访问并处理接收到的消息。
多线程(Multithread),如果一个进程中有多个线程同时存在,就叫做多线程了。
多任务(Multitasking),即多个程序看起来好像是在同时执行,其实并不是同时的,只不过因为时间太短,人类感觉不出来而已。其原理是操作系统分配给每个线程一个非常短(大约百分之秒)的时间片,每个线程轮流切换执行,这个过程叫做场境转换(Context Switching)。
场境转换(Context Switching),是指:
1、运行一个线程直到该线程的时间片用完,或者这个线程必须等待其他的资源;
2、保存这个线程的场境;
3、取出其他线程的场境;
4、只要有线程在等待执行,就会不停的重复以上过程。
Raw Input Thread(RIT), 是指用来接收所有由键盘和鼠标产生的事件(Event)的线程,它是一个特殊的系统线程,每当RIT接收到处理器发出的硬件(Hardware)事件,它 就把那些事件放到相应线程的虚拟输入队列中。因此,应用程序的线程通常是不用等待它的硬件事件的。
事件驱动(Event-Driven)编程,Windows-based的应用程序运行后,就会一直等待,直 到有用户发布命令(例如:按一个按钮或选中一个菜单)之类的事件发生,这就叫做事件驱动编程(Event-Driven Programming)。它同DOS下的应用程序的最大区别就是:DOS下的应用程序是通过命令行加参数的方法来控制应用程序的执行,而Windows -based的应用程序是通过图形用户界面(GUI)来控制应用程序的执行。用户所产生的事件,在程序里就会转化为消息,不同的事件产生不同的消息,从而 可以产生不同的响应。
终于讲完这一节了,大家看得明白吗?如果不明白的话,那就一字一句的从头到尾再看一遍吧。如果还不明白,那就请跳过这一节吧,我在后面的章节中还会逐步解释这些概念的。在本章的最后一节我将会举一个具体的程序来说明Windows-based应用程序的结构和组成元素。
以下是本节出现的专业名词
应用程序 = Application
指令 = Instruction
进程 = Process
内存 = Memory
系统资源 = System Resource
文件 = File
管道 = Pipe
通讯端口 = Communications Port
信号 = Semaphore
线程 = Thread
物理内存 = Physical Memory
虚拟内存 = Virtual Memory
映射 = Map
虚拟地址 = Virtual Address
虚拟内存管理器 = Virtual Memory Manager
代码 = Code
动态链接库 = Dynamic-Link Library,即DLL
数据 = Data
堆栈 = Stack
多任务 = Multitasking
父线程 = Parent Thread
子线程 = Child Thread
多线程 = Multithread
场境转换 = Context Switching
虚拟输入队列 = Virtual Input Queue
处理器 = Processor
操作系统 = Operating System
消息 = Message
队列 = Queue
Raw Input Thread = RIT
事件 = Event
硬件 = Hardware
事件驱动 = Event-Driven
事件驱动编程 = Event-Driven Programming
图形用户界面 = GUI
Windows下的程序的结构和组成元素
Windows下的程序的基本组成元素是代码, 用户界面资源(User Interface Resource)和动态链接的库模块(Library Module)。
代码,是应用程序的主要内容,Windows下的应用程序必须要有两个函数:
1、WinMain,它为操作系统提供了进入点(Entry Point),是所有Windows-Based应用程序都必须要有的函数。它也用来创建初始Window和启动Message检索;
2、Window Procedure,它用于处理所有从操作系统发送到Window的Message,每一个Window都有一个相关联的Window Procedure。Window Procedure用来决定Window的Client Area(即客户窗口,例如Notepad中用来写字的空白部分)显示什么以及如何响应用户的输入。Window Procedure处理Message时,既可以用专门添加的代码来处理Message,也可以直接把Message传递给默认的Window Procedure——DefWindowProc。一个Windows-Based应用程序可以包含多个不同名的Window Procedure。
用户界面资源,菜单(Menu),对话框(Dialog box)等图形用户界面的元素,就叫做资源。它们被当成模板(Template)储存在相应的可执行文件或DLL文件的只读(Read-Only)区域,当有需要时,Windows就调用这个资源区域并动态创建所需要的GUI元素。主要有以下几种资源:
Accelerator(快捷键表), 储存快捷键和相应的命令
Bitmap(位图),一种图形格式
Diablo Box,包含对话框的控件(Control), 布局和属性的细节
Icon(图标),一种特殊的位图
Menu(菜单),包含菜单及其选项的文本和布局
String Table(字符串表),储存字符串及其ID
Toolbar(工具栏),包含工具栏的布局和按钮的位图
Version(版本),储存程序的状态信息,例如程序名,作者,版权,版本号等
Cursor(光标),包含用于绘制光标的特殊的位图
库模块,主要是指在运行时可以被动态链接的二进制文件,即DLL。
默认的Window Procedure——DefWindowProc,是Windows系统提供的一个函数,用于处理某些通用的Win32-based应用程序的 Messages(例如最大化、最小话窗口,显示目录等)。如果DefWindowProc不能处理该Message,那么它就被忽略。
当一个应用程序被启动时,将会按顺序发生下列事件(上一节也提到过这个问题)
1、操作系统创建一个新进程和一个起始线程;
2、应用程序的代码被载入内存;
3、DLL也被载入内存(如果有的话);
4、从物理内存分配其他项目(例如数据,堆栈等)的空间,并被映射到虚拟地址空间中;
5、应用程序开始执行。
在Windows-Based应用程序中,Windows是应用程序和用户之间传递信息的主要方法。Windows-Based的应用程序为了接收从系统队列传来的Message,是通过以下方法实现的:
1、当Windows-Based的应用程序启动后,操作系统和这个应用程序就通过进入点(WinMain函数)联系起来。
2、应用程序创建一个或多个Windows,每个Window都包含有一个Window Procedure函数,用来决定Window显示什么以及Window如何响应用户的输入。
3、有专门的代码将Message队列中的Message循环检索出来,并传递给相应的Window Procedure,而不是直接传给Window。这样就可以使应用程序在Message被送到Window之前预先处理它。
到了下一节,我们将会用一个简单的源程序说明以上元素和步骤。
以下是本节新出现的专业名词
用户界面资源 = User Interface Resource
库模块 = Library Module
进入点 = Entry Point
客户窗口 = Client Area(例如Notepad中用来写字的空白部分)
菜单 = Menu
对话框 = Dialog box
模板 = Template
只读 = Read-Only
控件 = Control
快捷键表 = Accelerator
位图 = Bitmap
图标 = Icon
字符串表 = String Table
工具栏 = Toolbar
版本 = Version
光标 = Cursor
动态链接 = Dynamic Linking
源程序示例
本节列出了一个简单的源程序,来说明上两节的内容。请大家结合上两节的内容来看看下面的源程序,不需要完全看懂,只用理解大概的框架和流程就行了,注意黑体字部分。源程序如下:
// 摘自http://msdn.microsoft.com/library/partbook/win98dh/thewinmainprocedure.htm
// 包含头文件windows.h
#include <windows.h>
// 预先声明Message Handler,可以叫做任何名字,这里是MyWindowProcedure
LRESULT CALLBACK MyWindowProcedure(HWND,UINT,WPARAM,LPARAM);
// 以下是所有Windows程序都需要的WinMain函数
// WinMain主要用来实现三个功能:
// 1. 注册Window Class;
// 2. 在内存中创建Window并初始化Window的属性;
// 3. 创建一个Message Loop来检查Message Queue中有没有该Window的Message。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)
{
static char szAppName[] = "WinHello"; // 定义一个字符串
HWND hwnd; // 定义一个Window Handle变量
MSG msg; // 定义一个Message结构的变量,用来储存Message的信息
WNDCLASS wc; // 定义一个Window Class数据结构,用来储存Window Class的属性
//下面这段代码用来定义Window的属性,例如Message Handler的地址、窗口背景、光标和图标等
wc.style=CS_HREDRAW|CS_VREDRAW; // 设置style: 当窗口改变大小时就重新绘制窗口
wc.lpfnWndProc=(WNDPROC)MyWindowProcedure; // 设定Window Procedure
wc.cbClsExtra=0; // 用来储存Class Structure后的额外的数据,这里不需要
wc.cbWndExtra=0; // 用来储存Window Instance后的额外的数据,这里不需要
wc.hInstance=hInstance; // Window Procedure所在的Instance
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION); // class的图标
wc.hCursor=LoadCursor(NULL,IDC_ARROW); // class的光标
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); // 背景刷
wc.lpszMenuName=NULL; // 菜单资源的名字,这里没有
wc.lpszClassName=szAppName; // 应用程序的名字
// 注册Window,通过调用API函数RegisterClass来实现
// 注册Window Class的一个目的就是将Window和Window Procedure关联起来
RegisterClass(&wc);
// 注册Window Class后,WinMain就调用CreateWindow函数来创建应用程序的Window
hwnd=CreateWindow(
szAppName, // 已注册的Class名字
"Hello, World – Windows_98 Style", // Window名字
WS_OVERLAPPEDWINDOW, // Window风格
CW_USEDEFAULT, // Window起点的X坐标
CW_USEDEFAULT, // Window起点的Y坐标
CW_USEDEFAULT, // Window的宽度
CW_USEDEFAULT, // Window的高度
HWND_DESKTOP, // 父窗口的handle
NULL, // 菜单的handle
hInstance, // 应用程序instance的handle
NULL // window-creation数据的指针
);
// 以下两条语句用来显示Window
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
// 用while循环语句来检索并发送Messages
// 从Message Queue中检索Message,并将它放到变量msg中。
// 当收到"WM_QUIT"这个Message时,GetMessage函数就返回0,循环结束。而且WinMain函数也结束,程序终止。
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg); // 将Virtual-Key Messages转化为Character Messages
DispatchMessage(&msg); // 将Message发送到Window Procedure
}
return msg.wParam;
}
// MyWindowProcedure函数处理WM_PAINT和WM_DESTROY这两个Message,然后必须调用DefWindowProc去处理其他Message
LRESULT CALLBACK MyWindowProcedure(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps; // 定义一个PAINTSTRUCT结构的变量,用来储存绘制Window的Client Area的信息
HDC hdc; // 定义一个HDC变量
LPCTSTR text="Welcome!"; // 定义一个LPCTSTR类型的字符串指针
// 用switch语句来处理WM_PAINT和WM_DESTROY这两个Message
switch(message)
{
case WM_PAINT:
// 下面5条语句是用来在屏幕上输出文字的,我们在后面的章节会详细讨论这个问题的,这里就不多说了
hdc=BeginPaint(hwnd,&ps);
RECT rect;
GetClientRect(hwnd,&rect);
TextOut(hdc,(rect.right-rect.left)/2,(rect.bottom-rect.top)/2,text,strlen(text));
EndPaint(hwnd,&ps);
return 0;
// 处理退出消息
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// 调用默认的Window Procedure,使所有Message都可以被处理
return DefWindowProc(hwnd,message,wParam,lParam);
}
运行上面程序的步骤:
1、选菜单 File-->New...-->Projects-->Win32 Application
2、在Project Name中输入vchack_01_002_003(其它名字也行)
3、其他地方就保留默认值就行了,然后按"OK"
4、选中"An empty project",然后按"Finish"
5、再按次"OK"
6、按Toolbar上的按钮"New Text File"新建一个空白文件
7、将以上源程序复制到那个空白文件中,然后按Toolbar上的按钮"Save"来储存文件,文件名为vchack_01_002_003.cpp
8、按左下角的"FileView",然后按"vchack_01_002_003 files"旁边的"+"号展开这个目录
9、在"Source Files"上按鼠标右键,选"Add Files to Folder..."
10、选中vchack_01_002_003.cpp这个文件,然后按"OK"
11、选"Build"菜单中的"Build vchack_01_002_003.exe"
12、选"Build"菜单中的"Execute vchack_01_002_003.exe"来运行这个程序
以下是本节新出现的专业名词
类 = Class
窗口类 = Window Class
数据结构 = Data Structure
消息处理器 = Message Handler
实例 = Instance
句柄 = Handle
工程 = Project
MFC简介
微软基础类库(Microsof Foundation Class Library)和Visual C++提供了一个创建各种各样应用程序的环境,并简化了其中部分工作。MFC Library是Class的集合,大约有250个Class,在很大程度上扩展了C++语言;MFC Library也是一个应用程序框架(Application Framework),它定义了应用程序的结构(当然你也可以用源程序一行一行地写出自己的应用程序结构,不过这样比较麻烦),并可以处理应用程序的一些常规任务。
如果你想用MFC进行程序开发,首先你必须熟悉MFC所包含的Class以及各个Class之间的关系。MFC Class是有层次的(MFC的层次图请看http://msdn.microsoft.com/library/devprods/vs6/visualc/vcmfc/_mfc_hierarchy_chart.htm,请大家务必要看,最好把它保存下来,以便日后查找),有些Class可以直接使用,而有些Class是作为其他Class的基类(Bass Class)一般不直接使用。为了学习的方便,一般将MFC Class划分为以下几个种类:
CObject-Derived Classes
Application Architecture Classes
User-Interface Classes
General-Purpose Classes
ActiveX Classes
Database Classes
Internet Classes
Global Afx Functions
以上划分的种类之间决不是相互独立的,大多数的MFC Classes是直接或间接从CObject Class派生的,CObject Class是MFC Library中最基本的Class。
下一节我将会分别对以上几个种类的Classes做一个简单的介绍,然后我还会分别用1至2章来详细介绍上面的几种Classes。这一节的内容比较少,请大家仔细看看MFC的层次图。MFC的命名规则是:Class名以C开头,其他地方顾名思义。
以下是本节新出现的专业名词
微软基础类库 = Microsof Foundation Class Library
微软基础类 = Microsof Foundation Class (即MFC)
应用程序框架 = Application Framework
基类 = Bass Class
MFC的层次、分类和作用
心情随笔:本节有很多专业名词,其实这些单词从字面上并不难理解,例如"Document",中文是" 文本"的意思,但假如把"Document Class"直接翻译成"文本类"的话,可能会把很多人搞混淆了,我觉得"文本类"比"Document Class"更难理解,正因为如此,所以我决定不把那些容易搞混淆的专业名词直译成中文了。我非常反感市面上的一些电脑书完全直译国外作品,可能是由于翻 译者的电脑水平不行,把一些专业名词凭空想象,例如有的把"Serialization"翻译成"序列化",有的又翻译成"串行化",完全脱离了原意。
本节将简要介绍MFC所包含的主要几种Class,大家最好要记住MFC的分类和各个Class的作用(特别是CObject派生的(CObject-Derived) Classes,应用程序结构(Application Architecture) Classes,用户界面(User-Interface) Classes这三种,一定要记住),这是后面章节的基矗大家现在无需知道各个Class的使用方法,因为我会在后面详细说明的。注:以下内容有部分摘自MSDN,其实我也记不住那么多Class,一般是有需要才去查帮助文件的。
一、CObject派生的(CObject-Derived) Classes
CObject是MFC大多数Class的基类,它主要提供了一些基本功能,主要包括:
Serialization,指把对象(Object)从存储媒体(例如磁盘上的文件)中读出或写入的过程;
Run-time Class信息,指从CObject派生的对象包含有在运行时可以访问的信息;
诊断输出,指CObject提供了一些输出函数,这些函数可以输出程序执行过程中的一些信息,可以帮助你调试程序。
从CObject派生的类为MFC应用程序提供了基本的结构和功能,重要的有以下几种:
类别 | 基类 | 描述 |
Command Targets | CCmdTarget | 用于处理用户请求 |
Applications | CWinApp | 代表应用程序的核心 |
Documents | CDocument | 包含应用程序的数据集 |
Windows | CWnd | 主要用于图形用户界面(GUI)的对象,可以处理常见的Windows Messages |
Frames | CFrameWnd | 用于应用程序的主要Window框架 |
Views | CView | 用于显示数据并于Document对象交互 |
此外,CObject-Derived Class还包括用于菜单、文件服务、图形等方面的Class。
MFC也包含了一些不是从CObect派生的类,这些类相对来说可以节省开销,主要分为以下几种:
1、用于常规编程的实用类,例如:CString, CTime, CTimeSpan, CRec, CPoint, CSize;
2、MFC结构的支持类,例如CArchive, CDumpContext, CRuntimeClass, CFileStatue, CMemoryState
3、用户定义指针的集合类,例如CTypedPointerArray
二、应用程序结构(Application Architecture) Classes
应用程序结构Class代表应用程序的基本结构元素,主要包括CWinApp, CDocument, CCmdTarget和CWinThread。当应用程序开始运行时,这些Class是最先被初始化的,它们都有很重要的作用。
1、CWinApp, 代表应用程序自己,所有的MFC应用程序都从CWinApp派生一个Class。根据应用程序框架(Framework)的种类,应用程序的对象(Object)要完成以下工作:
(1) 初始化(Initialize)应用程序
(2) 建立Document Template结构
(3) 循环检索Message Queue中的Message并派送这些Message到相应的地方
(4) 当应用程序退出时要进行"清理"工作
2、CDocument, 它是使用Document/View结构的应用程序中的Document的基类。这里的Document代表程序中的数据,是一个抽象概念,我们在开发程序时必须考虑数据如何储存到Document中。
3、CCmdTarget,它是MFC的Message映射的基础Class,从CCmdTarget派生的类可以成为Command Messages的目标。Command Messages是指由用户选择菜单或按钮等行为产生的Messages。
4、CWinThread,它的成员函数可以使MFC应用程序创建和管理线程。
三、用户界面(User-Interface) Classes
用户界面Classes主要包含Windows-based应用程序的一些可视性元素,例如:窗口、菜单、对话框、控件(Control)等,它还封装(Encapsulate)了Windows Device Context对象和Graphics Device Interface(GDI)对象。
用户界面Class包括CWnd, CView, CGdiObject和Menu这几个主要Class:
CWnd,它是所有MFC Windows的基类,它定义了Window的基本功能和Window对大部分Message的默认响应。CWnd可以直接用来派生其他Class,但通常情况下,Class是从CWnd派生的Class派生的,从CWnd派生的Class主要有:
CFrameWnd,主要用于单文档界面(Single Document Interface, 例如写字板之类的程序,一次只能打开一个Window);
CControlBar,是工具栏,状态栏等控件的基类;
CDialog,提供对话框的功能;
CButton, CListBox, CScrollBar等,主要用于按钮,列表框,滚屏栏等控件。
CView,是Document/View(一种应用程序的结构,下节再讲)应用程序的视图的基类;
CGidObject,它包含一些用于显示输出的对象(例如Pen, Brush, Font等),使MFC应用程序可以创建和使用这些对象。GDI最大的好处就是提供了设备无关性(Device-Independent),使到开发人员无需考虑不同设备的问题。
CMenu,主要用于提供菜单界面,通过CMenu,应用程序可以在运行时动态改变菜单的内容。
四、常规用途(General-Purpose) Classes
General-Purpose Classes包括各种各样的数据类型,常用的有:
CFile,用于文件的输入/输出
CString,用于管理字符串变量
CException,用于处理Exception
CByteArray, CIntArray, CStringArray, CStringList, CObList, 用于数据结构,例如数组和列表
CPoint, CSize, CRect, CTime, CTimeSpan,杂项
五、ActiveX Classes
ActiveX Classes可以简化ActiveX的编程和ActiveX API的访问,ActiveX的主要作用和功能是:
创建ActiveX控件和ActiveX控件容器
通过自动化(Automation),是一个程序控制另一个程序
创建包含有多种数据类型(例如文字、图片、声音等)的文档,既复合文档
创建可以嵌入复合文档的OLE Object
使用拖放(Drag-and-Drop)方式可以在两个应用程序之间复制数据
ActiveX Class的分类如下:
ActiveX Control Classes
包括COleControlModule, COleControl, CConnectionPoint, CPictureHolder, CFontHolder, COlePropertyPage, CPropExchange, CMonikerFile, CASyncMonikerFile, CDataPathProperty, CCachedDataPathProperty, COleCmdUI, COleSafeArray
Active Document Classes
包括CDocObjectServer, CDocObjectServerItem
ActiveX-related Classes
包括COleObjectFactory, COleMessageFilter, COleStreamFile, CRectTracker
Automation Classes
包括COleDispatchDriver, COleDispatchException
Container Classes
包括COleDocument, COleLinkingDoc, CDocitem, COleClientItem
OLE Server Classes
包括COleServerDoc
OLE Drag-and-Drop And Data Transfer Classes
包括COleDropSource, COleDataSource, COleDropTarget, COleDataObject
OLE Common Dialog Classes
包括COleDialog, COleInsertDialog, COlePasteSpecialDialog, COleLinksDialog, COleChangeIconDialog, COleConvertDialog, COlePropertiesDialog, COleUpdateDialog, COleChangeSourceDialog, COleBusyDialog
创建ActiveX比较难,我会在"提高篇"中详细讨论的。
六、数据库(Database) Classes
数据库编程是非常枯燥的,但我们不得不承认数据库非常有用,连接数据库然后访问数据是常用的数据库编程方法。MFC提供了一些类,这些类可以通过开放式数据库连结(Open Database Connectivity, 即ODBC)和数据访问对象(Data Access Object, 即DAO)来操作数据库。
Database Classes主要包括CDatabase, CDaoDatabase, CRecordset, CDaoRecordset。
CDatabase或CDaoDatabase的Object代表一个和数据源(Data Source)的连接,通过这个Object就可以操作数据源了。这里的数据源是指数据库中的数据的实例(Instance)。
CRecordset或DaoRecordset的Object代表从数据源中选中的数据的集合,叫做Recordset。CRecordset和DaoRecordset的Object有两种形式:
Dynasets, 动态的,假如数据库被更新,Recordset也同步被更新;
Snapshot,静态的,它只反映了在Recordset被调用时的状态,不会随着数据库的更新而更新。
CDaoRecordset还可以直接代表数据库的表(Table)。
七、Internet Classes
Internet Classes不但可以用于Internet,还可以用于Intranet(企业内部网)。MFC包括WinInet APIs(提供客户端的Class)和Internet Server API(即ISAPI,提供服务器端的Class)。
客户端的Class主要有以下几个:
CInternetSession, 创建并初始化一个或多个同步的Internet Session(会话),它有3个主要函数GetHttpConnection, GetFtpConnection和GetGopherConnection(这3个函数的作用大家可以顾名思义)。
CHttpConnection, 管理应用程序对HTTP服务器的连接。
CFtpConnection, 管理应用程序的FTP连接,它包含了一些用于搜索远程目录和文件的函数。
CGopherConnection,管理应用程序的Gopher连接,它也包含了一些用于搜索不同类型文件的函数。
CFileFind,它是CFtpFileFind和CGopherFileFind的基类,提供了搜索和定位的功能,并可返回文件的信息,它们都还支持通配符查询。
服务器端的Class主要有以下几个:
CHttpServer, 可用于创建和管理一个服务器扩展(Server Extension)DLL,也叫做Internet服务器应用程序(Internet Server Application,即ISA)。ISA一般用来扩展一个Internet服务器的能力。
CHttpServerContext, 被CHttpServer用来封装单个客户端请求的实例(Instance)。
CHttpFilter, 这个Class可以用来创建一个具有过滤客户数据功能的DLL。
CHttpFilterContext,被CHttpFilter用来封装单个客户通知(Notification)的实例(Instance)。
CHtmlStream, 封装HTML数据缓冲区(Buffer),该Buffer是被CHttpServer用来应答客户的。
八、全局Afx函数(Global Afx Functions)
Global Afx Functions不属于任何Class,它们以Afx开头,可以在应用程序的绝大多数地方被直接调用(这点和Class的成员函数有很大不同)。常用的全局Afx函数有:
AfxAbort(), 无条件中断应用程序
AfxMessageBox(), 显示一个消息框
AfxGetApp(), 返回一个指向Project的CWinApp Object的指针
AfxGetAppName(), 返回应用程序的名字,类型为一个指向字符串的指针
AfxGetMainWnd(), 返回指向主框架窗口(Main Frame Window)的指针
AfxGetInstanceHandle(), 返回当前应用程序的实例(Instance)的句柄(Handle),即HINSTANCE
以下是本节新出现的专业名词
派生 = Derive
连续化 = Serialization
对象 = Object
集合类 = Collection Classes
框架 = Frame
框架 = Framework
重载 = Override
初始化 = Initialize
Document
Command Messages
封装 = Encapsulate
控件 = Control
设备环境 = Device Context
图形设备接口 = Graphics Device Interface (GDI)
单文档界面 = Single Document Interface
设备无关性 = Device-Independent
异常或例外 = Exception
ActiveX控件 = ActiveX Control
ActiveX控件容器 = ActiveX Control Container
自动化 = Automation
拖放 = Drag-and-Drop
数据源 = Data Source
实例 = Instance
企业内部网 = Intranet
客户端 = Clien-Side
服务器端 = Server-Side
会话 = Session
服务器扩展 = Server Extension
Internet服务器应用程序 = Internet Server Application,即ISA
通知 = Notification
缓冲区 = Buffer
主框架窗口 = Main Frame Window
实例 = Instance
句柄 = Handle
Document, View和Application Framework
在MFC中,Document, View和Application Framework是3个非常重要的概念。顾名思义,Application Framework就是应用程序框架,你可以用这个框架来建立自己的Windows程序,可以节省不少时间。你也可以不用框架而用手工一行一行的写出源代码,这样做的话工作量就太大了。如果你用Application Framework的话,框架就会自动产生一些源程序代码和标准的用户界面,你需要做的工作就是提供剩余的代码,完成特定的任务。
另外,用MFC编程还要掌握一种重要的结构,即Document/View结构。在这里,Document是指用户正在使用的数据,它是一个Data Object;View是指用户所见到的Document的视图,它是一个Window Object。例如在Excel中,同一数据可以制成不同的报表图(例如饼状图,条形图),而且当数据改变时,报表图也随之改变。使用Document/View结构可以利用Application Frame以及MFC的很多好处。而不使用Document/View结构,对于某些简单的程序可以提高性能,并可减少程序的大校总的来说,Document/View结构就是通过CDocument和CView来为Document和View提供框架。
MFC中的应用程序主要可以分成两类,即SDI(单文档界面,例如记事本)和MDI(多文本界面,例如 Word)。SDI应用程序一次只能打开一个文档框架窗口,而MDI应用程序在一个主框架窗口中可以有多个子窗口,这些子窗口可以包含不同类型的文档。 MDI比较复杂,我会在《提高篇》中再详细讨论这个问题的,在《入门篇》中我们只讨论SDI。
在SDI应用程序中,主要有以下Object(结合上一节的内容有助于理解):
1、Document:从CDocument派生,代表应用程序的数据;
2、View:从CView派生,代表应用程序数据的"外貌",用户通过View来察看和操作Document;
3、Frame Window:从CFrameWnd派生,提供了用来显示View的文档框架窗口(Document Frame Window)。在SDI中,Document Frame Window也就是应用程序的主框架窗口(Main Frame Window),View就是显示在Frame Window里面的;
4、Document Template:在SDI中是从CSingleDocTemplate派生的,CSingleDocTemplate又是从CDocTemplate派生的,主要用于创建和管理某种类型的Document,每个Document Template创建和管理一个Document;
5、Application:从CWinApp派生,控制上面的4种Object,并指定应用程序的行为,例如初始化等。Application Object也用来响应用户的行为(例如由用户产生的Command Message)。
在MFC应用程序中,并不是需要以上所有的Object,以上那些Object是可以按照不同的规律来组合使用的。例如在非Document/View结构的程序中,有以下两种情况:
1、一个CWinApp Object和一个对话框(要Modal的,即类似文件打开那种对话框),在这种应用程序中,对话框用来显示和储存数据;
2、一个CWinApp Object、一个Main Frame Window(CFrameWnd)和一个View,在这种应用程序中,View用来定位数据储存和显示的地方。
注意:在非Document/View结构的程序中,一般是以重载CWinApp::InitInstance函数开始的,重载CWinApp::InitInstance函数的目的就是创建对话框或窗口。
操作系统、应用程序和应用程序组件之间的通讯是通过不同种类的Message来实现的。例如,当创建一个应用 程序的实例时,操作系统会发送一系列的Message给应用程序,应用程序就会响应相应的Message来初始化自己。键盘和鼠标也会使操作系统产生 Message并把这些Message发送给相应的应用程序。用户界面组件(例如按钮)也会产生Message并将Message发送给他们的父窗口。最 重要的两种Message是Window Message和Command Message。MFC通过CWnd和CWnd的派生类(例如:CView, CFrameWnd等)来提供对Window Message的支持,通过从CCmdTarget派生的类来提供对Command Message的支持。
Application Framework会把Message和处理该Message的函数联系起来,这样MFC就可以把Message映射到处理该Message的函数。每个Windows Message都有一个预先定义的宏(Macro),包括一个隐含的ID和处理函数的名字;而每个Command Message的Macro包括一个指定的ID和处理函数的名字。请看下面的源程序:
BEGIN_MESSAGE_MAP(CMyView, CView) //这是一个Macro,标志Message映射的开始,注意参数为Message映射的Class(这里是CMyView)及其基类(这里是 CView)的名字。这样的话,假如在CMyView中找不到该Message的处理函数,Framework还会在CView中继续寻找该 Message的处理函数。
ON_WM_CREATE() //这是一个处理Window Message的宏,不需要Message的ID和它的处理函数的名字作为参数(因为这两个参数是隐含的),在这里,ON_WM_CREAT所处理的 Message是WM_CREATE,该Message的处理函数是OnCreate。大家分析一下宏(ON_WM_CREATE),Message (WM_CREATE)和Message的处理函数(OnCreate)这三者之间的命名规则。
ON_COMMAND(ID_APPLY_SEQUENCE, OnApplySequence) // 这是一个处理Command Message的宏,需要Message的ID(ID_APPLY_SEQUENCE)和处理该Message的函数的名字(OnApplySequence)这两个参数
END_MESSAGE_MAP() //这是结束Message映射的宏
你可以用Visual自带ClassWizard或者WizardBar这两个工具来添加Message映射,也可以用手工的方法来添加Message映射,我会在后面的章节中详细讨论Message的问题的。
以下是本节新出现的专业名词
应用程序框架 = Application Framework
单文档界面 = Single Documnet Interface, 即SDI
多文档界面 = Multiple Documnet Interface, 即MDI
文档框架窗口 = Document Frame Window
主框架窗口 = Main Frame Window
宏 = Macro
用AppWizard来创建第一个应用程序
几个小提示:
1、在使用Visual C++时,大家千万不要忽略了鼠标右键的功能,它会根据不同的情况来给出不同的快捷菜单,十分方便;
2、如果你在源程序发现了不明白的地方(例如函数,Macro,关键字等),你就把光标停留在他们上面,然后按"F1"键就可以直接跳到相关的帮助文件了;
3、你可以根据自己的需要来定制界面,Visual C++会"记住"你所做的改变的;
4、把鼠标停留在函数,Macro,关键字等地方或者双击它们,也会有些小作用,大家自己体会吧。
Visual C++提供了很多向导来帮助你完成各种各样的程序,以后在需要使用向导时,我会详细介绍向导的使用方法的。现在我就举个例子,来说明AppWizard的使用方法。
步骤如下:
1、运行Visual C++,选择"File"菜单中的"New"命令,会出现一个"New"对话框;
2、在"New"对话框中选中"Project",然后选"MFC AppWizard (exe)",在"Project Name"中输入"vchack_01_004_001",在"Location"中可以改变Project的目录,其他地方保留默认值就可以了,然后按"OK"
3、在"MFC AppWizard - Step 1"对话框中,选择"Single documnet"(因为我们现在要创建的是SDI应用程序,如果要创建MDI应用程序那就要选"Multiple document"了;如果要创建对话框类型的应用程序那就要选"Dialog based"),其他地方保留默认值,然后按"Next";
4、在"MFC AppWizard - Step 2 of 6"对话框中,直接按"Next";
5、在"MFC AppWizard - Step 3 of 6"对话框中,去掉"ActiveX Controls"的选中符号,然后按"Next";
6、在"MFC AppWizard - Step 4 of 6"对话框中,直接按"Next";
7、在"MFC AppWizard - Step 5 of 6"对话框中,你会看到"How would you like to use the MFC library",如果你选择"As a shared DLL",这样产生的可执行文件较小,但在运行时需要DLL文件(Mfc42.dll和Msvcrt.ll),也就是说,这个程序在没有DLL文件 (Mfc42.dll和Msvcrt.ll)的电脑上是不能运行的。如果你选择"As a statically linked library",产生的可执行文件较大但不需要DLL文件(Mfc42.dll和Msvcrt.ll)的支持。在这里我们选择"As a shared DLL",然后按"Next";
8、在"MFC AppWizard - Step 6 of 6"对话框中,按"Finish";
9、在"New Project Infomation"对话框中,会显示你刚刚创建的Project的信息,按"OK",AppWizard就会自动创建一些开始文件并返回到主界面。如果按"Cancel",你就可以返回上面的步骤;
10、在主界面中的左边,你可以在"ClassView","ResourceView","FileView"中切换(我用的是Visual C++ 6.0),如果你用的是Visual C++ 5.0,你还会看到一个"InfoView"。展开这些窗口里面的"+"号,看看AppWizard为你创建了些什么东西;
11、在"Build" 菜单,选"Build vchack_01_004_001.exe"来创建一个可执行的exe文件;
12、在"Build" 菜单,选"Execute vchack_01_004_001.exe"来运行这个程序。
大功告成,AppWizard为你建立了以下文件:
*.dsw,是所有Project的总和,DSW是Developer Studio Workspace的简写;
*.dsp,是单个Project文件,DSP是Developer Studio Project的简写;
*.cpp,源程序;
*.h,头文件;
*.rc,资源文件;
另外,还有一个Debug目录或Release目录,包含可执行文件和其他一些编译文件。
如果你想关掉这个Poject的话,就选"File"菜单中的"Close Workspace"。选"Open Workspace"可以打开一个Project。
以下是本节新出现的专业名词
向导 = Wizard
应用程序框架 = Application Framework
单文档界面 = Single Documnet Interface, 即SDI
多文档界面 = Multiple Documnet Interface, 即MDI
文档框架窗口 = Document Frame Window
主框架窗口 = Main Frame Window
宏 = Macro
调试应用程序(上)
当你创建应用程序时,你可以选择创建Debug版或Release版的应用程序,它们之间的区别是它们使用了 不同的DLL文件,Debug版包含了一些调试信息,一般是没有经过优化的,而且有证书限制,不能用于发行;而Release版是经过优化的,用于正式发 行时使用,一般不用于调试。创建一个Debug版的Project的步骤是:
1、然后选择"Project"菜单中的"Settings";
2、在左上角的"Settings For"中选择"Win32 Debug",然后按"OK";
3、在"Build"菜单选择"Set Active Configuration";
4、编译你的程序。
在编写程序时,大家一般都会碰到两种错误,一种是语法或拼写错误,VC的Compiler会查出这类错误。还有一种错误就是逻辑错误,Compiler不能检查出这种错误,不过Visual C++提供了十分强大的调试功能,来帮助你检查出此类错误。有关Debug的命令可以在以下菜单中找到:
1、"Build"菜单中的"Start Debug",用于开始调试;
2、当"Debug" 正在进行时,"Build"菜单会变为"Debug"菜单,用于控制程序的执行;
3、"View"菜单中的"Debug Windows",用于显示几种不同的Debug窗口;
4、"Edit"菜单中的"Breakpoints",用于控制断点。
另外,在Debug过程中,还会出现一个浮动的"Debug"工具栏,可以方便你的调试工作。如果"Debug"工具栏没有自动出现的话,你也可以手工调出它,方法是:在工具栏的空白处按鼠标右键,然后在弹出菜单中选择"Debug"。
在Debug过程中,你选择一些特殊的窗口用于显示各种不同的调试信息,它们分别是:
1、Output,用于显示关于Build的过程的信息;
2、Watch,用于显示变量或表达式的名字和值;
3、Variaables,用于显示变量的信息;
4、Registers,用于显示CPU寄存器的内容;
5、Memory,用于显示当前内存的内容;
6、Call Stack,用于显示所有未返回的函数的堆栈;
7、Disassembly,用于显示原程序的汇编语言代码。
以上窗口的都可以通过浮动的"Debug"工具栏上的按钮调出。你也可以通过"Tools"菜单上的"Option",然后选"Debug"来修改以上窗口的显示选项。
在Debug过程中,你还可以使用一些对话框,他们的名字和作用如下:
1、Breakpoints,位于"Edit"菜单下,用于显示和控制断点;
2、Exceptions,位于"Debug"菜单下,用于显示系统和用户定义的Exceptions,并可以指定调试器如何处理这些Exception;
3、QuickWatch,位于"Debug"菜单下,用于显示或修改变量和表达式;
4、Threads,位于"Debug"菜单下,用于显示和控制应用程序的可以Debug的Thread。
从上面的介绍可以看出Visual C++的Debug功能的强大,足以令Unix或Linux程序员羡慕不已。下面就来简单介绍一下各种Debug工具的使用方法:
一、设置断点(是指程序暂停执行的位置)
1、在源程序中设置断点
把光标移到你想使程序中断的地方,然后按工具栏上的"Build MiniBar"上的"Insert/Remove Breakpoint"按钮来设置断点,设置断点后在那行源程序的左边会出现一个红色的点。如果一句源程序超过了一行,那你就必须在这句源程序的最后一行设置断点。
2、在函数的开头设置断点
在工具栏上的"Standard"上的"Find"对话框中输入函数的名字,找到那个函数,然后设置断点。
3、在函数的Reture位置处设置断点
在"View"菜单中,选"Debug Windows"->"Call Stack",然后将光标移到你想中断的函数,再设置断点。
4、在Label处设置断点
和"在函数的开头设置断点"的方法类似,只不过输入的是Label的名字。
5、激活和取消一个断点
在断点处按鼠标右键,然后选"Enable/Disable Breakpoint",如果你取消一个断点,那个被取消的断点的标记会变为空心的。
6、察看断点
选"Edit" 菜单下的"Breakpoint",就会显示所有断点的列表。如果你选中一个断点,然后按"Edit Code"就会跳到断点所在的源程序的位置。如果你清除某个断点前面的复选框(用鼠标或者选中该断点然后按按空格键),那个断点就会被取消。如果复选框的标记变为*号,那就说明当前的平台不支持断点。
注意:取消(Disable)和删除(Remove)断点的含义不同。
二、控制程序的执行
1、当应用程序的执行暂停在断点处时,可以用"Debug"菜单中的"Step Into"命令来执行下一条语句,执行完下一条语句后,程序又会被暂停执行。如果下一条语句是一个函数,那么就会执行该函数内的第一条语句。
2、还可以在源程序或Debug窗口中使用"Step Over", "Run To Cursor","Step Into Specific Function"来控制程序的执行。如果是使用"Step Into Specific Function"的话,程序会在选定的函数的开始处暂停。
三、查看变量
1、在Debug窗口中,把鼠标停留在变量名上面,就会弹出一个窗口,显示变量的值。
2、当程序暂停在一个断点时,在Debug窗口中,用鼠标右键单击变量,然后选"QuickWatch"->"Recalculate"即可。
3、在Debug窗口中,选择"View"->"Debug Windows"->"Watch",然后在"Watch"的"Name"处输入变量的名字(也可以直接把编量名拖到"Name"中)。如果变量是一个数组或对象,那么它前面就会有"+"或"-",你可以展开"+"来查看变量。
4、在Debug窗口中,选择"View"->"Debug Windows"->"Variables",然后再选"Variables"窗口中的"Auto"或"Locals"或"This"来查看相应的变量。
5、在"Watch"或"Variables"窗口中选中某个变量,然后选择"View"菜单下的"Properties",可以查看变量的其它信息。
四、改变变量的值
1、在Debug窗口中,选"Debug"->"QuickWatch",在"Expression"中输入变量名,然后按"Recalculate",再使用"Tab"键把光标移到"Value"处,输入新的值,然后按回车。
2、在"Watch"或"Variables"窗口中的"Value"处,也可输入变量的新的值。
五、查看Call Stack
1、在Debug窗口中,选择"View"->"Debug Windows"->"Call Stack",就会按照调用顺序显示所有的函数调用,当前的函数调用会显示在最上方。双击函数名可以直接跳到该函数的代码处。选中某个函数,然后选"Run to Cursor"命令,可以使程序执行到该函数的末尾;选"Insert/Remove Breakpoint"命令,可以在函数的末尾处设置断点。
2、选"Tools"->"Options"->"Debug",然后选"Parameter Value"或"Parameter Types"可以改变"Call Stack"的显示方式。
注意:在"Variables"窗口顶部的"Context"中的下拉菜单中也包含"Call Stack"函数,你可以使用该下拉菜单在个函数之间跳转,但不能反向跟踪Windows Messages。
六、执行到指定的地方
1、通过设置断点的方法。
2、把光标移到源程序中想暂停的地方,然后选"Build"->"Start Debug"->"Run to Cursor"。
3、在"Disassembly"或"Call Stack"窗口中也可以使用方法2中的步骤。
4、在"Standard"工具栏的"Find"中输入函数名,然后选"Build"->"Start Debug"->"Run to Cursor"。
5、在源程序窗口,把光标移到你下一步想运行的语句处,然后单击鼠标右键,再选择"Set Next Statement"。
注意,可以用"Run to Cursor"命令跳到前面的代码处,然后用不同的变量值来测试程序。
七、使用Browse Windows
VC的Browse Windows是用来显示符号(Symbol,例如Class, Function, Data和Macro)的信息,也叫做Browse Infomation。如果你在Build一个Project时打开了"Browse Info"选项,Compiler就会为Project中的每个程序文件的信息都创建一个相关的信息文件(.sbr),然后BSCMAKE工具(BSCMake.exe)把这些sbr文件编译成一个单独的信息文件(.bsc)。
当在Browse Windows中查看Browse Infomation时,Browse Windows会根据不同的信息而显示不同的窗口,你可以在Browse Windows中检查:
1、源程序中所有Symbol的信息;
2、Symbol在源程序中的定义行;
3、Symbol在源程序中的参考(Reference)行;
4、基类和派生类之间的关系;
5、调用函数和被调用函数之间的关系。
当你打开一个Project Workspace时,Project的Browse文件也会被自动打开。当然,你也可以通过设置来不自动打开Browse文件,以加快速度。设置方法如下:
1、激活或取消Compile时.sbr文件的创建
选"Project"->"Setting"->"C/C++",然后选中或取消"Generate browse info"选项。如果你取消"Generate browse info"选项的话,就不会产生.sbr文件,也不会更新.bsc文件。
2、激活或取消Compile时.bsc文件的更新
选"Project"->"Setting"->"Browse Info",然后选中或取消"Build browse info file"选项。为了加速编译,一般可以打开.sbr文件的创建而关掉.bsc文件的更新,到有需要时才打开bsc文件的更新。
注意,当创建了Browse文件后,你就可以使用"Browse"工具栏了。
3、打开或关闭Browse Infomation文件
选"Tools"->"Source Browser"或"Close Source Browser File"。
注意,如果你在使用Brwose Infomation文件,那么.bsc文件就会处于打开状态中,而且.bsc文件不会自动关闭。当.bsc文件处于打开状态时,它是不能被更新的。
八、使用Just-In-Time Debugging
如果使用了"Just-In-Time Debugging",那么你就可以不必在VC的窗口中来调试应用程序了。当你在VC以外的环境中运行应用程序时,如果应用程序出错了,"Just-In-Time Debugging"会自动调用VC的Debugger。使用方法是:选"Tools"->"Options"->"Debug",选中"Just-In-Time Debugging"选项,然后按"OK",然后重新Build这个程序。
注意:在NT环境下,必须有Administrator权限才能设置"Just-In-Time Debugging"选项。
以下是本节新出现的专业名词
Breakpoint = 断点
Register = 寄存器
Exception = 异常
Thread = 线程
Symbol = 符号
调试应用程序(下)
Visual C++和MFC还提供了一些比较高级的Debug技术:
一、使用MFC函数和Macro
Visual C++ 5.0以后的版本引入了对C运行库(C Run-Time Library)的Debug支持,这个新的Debug版本还提供了一些诊断服务,简化了Debug过程。下面将介绍一些具有诊断目的的Debug例程(Routine)和Macro。
1、C Run-Time Library的Debug支持
Visual C++对C Run-Time Library也提供了Debug支持,使你在Debug应用程序时可以直接进入Run-Time函数。C Run-Time Library也提供了一些工具来跟踪堆(Heap)的分配,定位内存的溢出(Memory Leak)以及发现其他有关内存的问题。有很多Heap检测技术已经从MFC Library转移到了C Run-Time Library的Debug版本中,如果你要使用Heap检测技术,你必须把MFC应用程序的Debug Build和Run-Time Library的Debug版本链接起来。
C Run-Time Library包括以下Debug报告函数:
(1) _CtrDbgReport和_CrelsValidPointer,用于验证和报告;
(2) _ASSERT和_RPTn,用于Debug Heap;
(3) Debug版本的malloc, free, calloc, realloc, new, delete,作用请参见C语言中的相关函数;
(4) _CrtCheckMemory和_CrtDumpMemoryLeaks等,用于监视Heap;
(5) _CrtSetDumpClient和_CrtSetAllocHook等,可以使你加入你自己的钩子函数(Hook Function)。
为了使用这些Routine,你必须使用_DEBUG标志,即使用"Win32 Debug"来编译你的程序(参见上一节开头部分)。在正式发行版中,这些Routine是不起作用的。C Run-Time函数在Windows 9x和NT中都可以使用。
2、Run-Time Debugging Routines
只有在运行Debug版的应用程序时,Run-Time Debugging Routines才被激活。而在Release版的应用程序中,Assertion是不起作用的,完全不会影响程序的执行速度。
(1) ASSERT Routine
ASSERT Routine主要用于确保一个假设的正确性,如果Assertion是错误的或者是失败的,Macro就会显示这个Assertion的消息框,包括源 文件的名称和在源文件中的位置等消息,还会给用户一个选择:中断或De