第一章
This presentation is the property of its rightful owner.
Sponsored Links
1 / 92

第一章 Windows 编程基础知识 PowerPoint PPT Presentation


  • 97 Views
  • Uploaded on
  • Presentation posted in: General

第一章 Windows 编程基础知识. 1.1 Windows 操作系统的特点 1 直观的图形化用户界面 Windows 应用程序的外观是由诸如 窗口 、 菜单 、 工具栏 、 状态 栏 、 滚动条 、 对话框 等标准图形元素构成的。程序运行中的人 机交互操作也都是通过这些标准图形元素进行的。我们将这样 的用户界面称为图形化用户界面 GUI ( G raphics U ser I nterface )。 GUI 使得应用程序的用户界面统一、友好、美观。. 2 丰富的设备无关操作 Window 程序的输出显示均为图形操作(包括文本操作)。各

Download Presentation

第一章 Windows 编程基础知识

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


第一章 Windows 编程基础知识


1.1 Windows 操作系统的特点

1 直观的图形化用户界面

Windows 应用程序的外观是由诸如窗口、菜单、工具栏、状态

栏、滚动条、对话框等标准图形元素构成的。程序运行中的人

机交互操作也都是通过这些标准图形元素进行的。我们将这样

的用户界面称为图形化用户界面 GUI(Graphics User Interface)。

GUI 使得应用程序的用户界面统一、友好、美观。


2 丰富的设备无关操作

Window程序的输出显示均为图形操作(包括文本操作)。各

类复杂的图形操作都是通过与物理设备无关的图形设备接口GDI

(Graphics Device Interface)完成的。每个图形操作都是在一个特

定的图形设备上下文(Device Context)中完成的。也就是说,通

过设备上下文句柄,能够调用图形设备接口 GDI所提供的相应

图形操作进行格式统一而具有特定功能的图形绘制操作,而这

些图形操作又可以通过对应的物理图设备驱动的支持,在指定

的设备上实现相应的图形输出。下图形象地示意了这种 GDI图

形接口的实现机制:


显示器驱动

打印机驱动

绘图仪驱动

GDI

虚拟设备


3 完善的多任务运行环境

Windows是一个多任务的操作系统。这种多任务表现在多个

不同进程(包括同一程序的多个进程实例)的同时运行和同一

进程中的多个线程的同时运行。这些同时运行的多任务对系统

资源的共享表现在:

CPU:通过系统分时实现多任务共享同一 CPU。

屏幕:通过可以重叠或平铺分布的窗口实现多个任务的视窗可

以共享同一屏幕,用户可以通过切换不同任务的窗口为活

动窗口,在不同任务之间进行切换。

内存:通过虚拟内存管理实现多任务共享有限的内存资源。多

任务之间可以进行手工和自动的数据交换和通信。


其中 Windows虚拟内存管理的实现如下:

⑴进程和内存空间

下面给出的图是在 Windows 95平台上,执行同一个 EXE 文

件的两个不同进程时的虚拟内存映射图。


0xFFFFFFFF

共享内存空间

0xC0000000

0x80000000

0x5F400000

0x10000000

堆栈

堆栈

可读/写全局内存

可读/写全局内存

0x00400000

进程A

硬盘

进程B

Windows

系统代码

Windows DLL

内存映射文件

MFC42.DLL

用户.DLL

交换文件

EXE文件


对于每个进程来说,只有低端的 2GB(0-0x7FFFFFFF) 的

地址空间是真正属于进程私有的。其中最低端的 4MB内存空间

是禁止访问的。进程运行期间所需要的堆栈、堆和可读/写的全

局内存以及应用程序的 EXE 文件和DLL文件都被映射到这 2GB

空间内。而高端的 2GB空间对所有的进程都是一样的,在这一

区间存放着所有进程共享的 Windows核心执行过程,虚拟设备

驱动程序(VxD)和文件系统代码以及一些重要的表(如映射页

表)都被映射到最高端1GB(0xC0000000 - 0xFFFFFFFF)空间中。

Windows DLL和内存映射文件在 0x80000000 - 0xBFFFFFFF 的内存

空间中。


由于低端的 2GB内存空间分配给特定的进程,所以一个进

程想要改变另一个进程的堆栈、全局内存或者堆空间的内容是

不可能的。

EXE和 DLL代码存放空间都有只读标记,因此,它们被映射

到多个进程是没有问题的。然而在最高端的 1GB空间有重要的

Windows可读数据,因此,这部分内存很容易受到错误程序的攻

击,例如毁坏系统表。在 0x80000000 - 0xBFFFFFFF地址空间中

存放的一个进程的映射文件也可能被另一个进程破坏。


在 Windows NT 中这些问题不会发生,因为在 Windows NT

中,进程只允许访问低端的 2GB空间,并且这 2GB的最高端和

最低端的 64KB空间是不允许访问的。同时高端的 2GB空间中

所存放的内容完全受保护。这就是为何提倡使用 Windows NT的

原因之一( Windows 2000 和 Windows XP 有类似的安全机制)。

⑵ 虚拟内存如何工作

① 为什麽要使用虚拟镜像技术

• 计算机不可能有数百个 GB的 RAM(物理内存)和数百个

GB的磁盘空间能满足多进程(每个进程 4GB)的需要。

• 每个进程的 4GB空间不会全部使用,更不会同时使用。


32位线性地址

位 31--22

位 21--12

位 11--0

每个进程拥有一个页表目录,它可以保存1024个页表地址

每个页表可保存1024个虚拟内存页地址

4096字节

32位地址

页表地址 标记

32位地址

页表地址 标记

20位

12位

20位

12位

CR3寄存器

Windows在CR3中为当前进程提供一个页表目录地址

② 如何实现虚拟镜像技术


• 32 位线性地址分三段,页表目录、页表和页内偏移量。

• 每页 4KB空间。

• 页以 4KB 为边界,即页的首地址必须是 4KB 的整倍数。

每个进程可以获得的虚拟内存空间为 4GB,每一个物理

地址的形成可以解析如下:

页表地址 = 页表目录地址+ 偏移量(第 22 - 31位),共

有1K 个页表。

页地址 = 页表地址+ 偏移量(地址第 12 - 21位),共有 1K*1K = 1M页。

物理地址 = 页地址 + 偏移量(地址第 0 - 11位),共有

1M*4KB = 4GB内存单元


• 每个页表入口都包含存在位(表示页是否在物理 RAM 中)

和读/写位(表示页中内容是否可读/写或只读)。

当需要访问此页内容时,根据“存在”位确定是否需要将此

页的内容从磁盘读入到此物理页中。如果页中内容有一段

时间未被访问,则根据虚拟管理的优化算法确定是否将页

中内容交换到磁盘中或直接放弃,使物理页空间可以被新

进程的页使用。在收回页面使用时,根据页的“读/写”位来

确定是将页中内容交换到磁盘中(例如,进程中所有可读

/写数据),或直接放弃(例如,程序 EXE代码和 DLL代

码,进程中的常量)。


③ 用户可以使用的内存操作函数

• 使用VirtualAlloc 进行内存的保留和占用,该函数的原型:

LPVOID VirtualAlloc( LPVOID lpAddress, DWORD dwSize,

DWORD flAllocationType, DWORD flProtect )

• 使用 VirtualFree 收回 VirtualAlloc 保留和占用的内存空间。VirtualFree 的原型:

BOOL VirtualFree( LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType );

• 使用GlobalAlloc 函数在Windows运行时堆中分配空间。

HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);

• 使用GlobalLock锁定GlobalAlloc 分配的内存空间一次,

并获取操作句柄。每锁定一次LockCount+1。

LPVOID GlobalLock(HGLOBALhMem);


• 使用GlobalUnlock 将内存空间解锁一次,LockCount–1,

当LockCount为0时,所分配的内存空间不再被锁定。

BOOL GlobalUnlock(HGLOBALhMem);

• 未锁定的内存地址(句柄)可以用GlobalFree 函数释放。HGLOBAL GlobalFree(HGLOBALhMem );

• 使用new分配内存空间。

• 使用delete释放由new 分配的内存空间。

• 内存映射文件:将一个地址范围直接映射到相应的文件。

当进程访问相应的内存页时,系统将分配 RAM,并从磁盘

中读入数据或将内存中数据写入磁盘,它可以用于进程间

共享。


•访问资源:资源是包含在 EXE 和 DLL代码中的,因此会

占用虚拟内存空间,而且这些空间在进程的生存期内是

不会被改变的,这就使得我们很容易读取一个资源。获

取资源的函数原型:

HGLOBAL LoadResource( HMODULE hModule,

HRSRC hResInfo);

参数:

hModule— 包含所取资源的模块句柄,NULL 表示从进程

中取资源。

hResInfo— 所取资源的句柄。

返回一个全局内存句柄HGLOBAL 可以安全地把它当作访

问存放资源的内存空间的索引。例如:


LPVOID lpvResource = (LPVOID)::LoadResource ( NULL,

::FindResource ( NULL,

MAKEINTRESOURCE(IDB_REDBLOCK),

RT_BITMAP ));

其中FindResource用于确定一个指定的资源位置,其原型:

HRSRC FindResource( HMODULE hModule, LPCTSTR lpName,

LPCTSTR lpType );

参数:

hModule — 包含所取资源的模块句柄,NULL 表示从进程中 取资源。


lpName— 所取资源的名字串。本例中是使用宏定义 MAKEINTRESOURCE 将资源标识 IDB_REDBLOCK

转换为资源名。

pType — 所取资源的类型。本例中使用的 RT_BITMAP表示

所取资源是一个位图资源。


4 灵活的消息处理机制

⑴ 队列化消息输入

Windows 操作系统将应用程序控制运行所需要各类信息以消

息的形式放在一个消息队列中,这个队列由操作系统管理。应

用程序只通过读取消息队列中的不同消息控制运行。

Windows 操作系统有一个系统消息队列,每个应用程序有一

个自己的消息队列,应用程序的消息来源:

①输入消息:如键盘、鼠标输入。这类消息通过系统消息

队列被送入应用程序消息队列。


②控件消息:用于与 Windows 的控件对象进行双向通信,

实现控件状态的变化。这类消息不通过系统消息队列,

可以通过应用程序消息队列,也可以直接发送到控件对

象上。

③系统消息:由系统管理事件(例如系统时钟)引起的消

息。这类消息中有些要通过系统消息队列送到应用程序

消息队列,如 DDE(Dynamic Data Exchange)消息;而另

一些消息直接送入应用程序消息队列,如创建窗口消息。

④用户消息:由程序员自己定义在应用程序中发出,并在

应用程序中响应处理的消息。这类消被直接送入应用程序消息队列。


⑵支持队列特征的消息驱动模型

Windows 操作系统主要包括三个基本内核元件:

①GDI(Graphics Device Interface):负责虚拟图形设备的操作,例如,屏幕绘图和打印。

②KERNEL:支持与操作系统密切相关的功能(如进程加载,文本切换,文件I/O,内存管理线程管理等)。

③USER:为所有的用户界面对象提供接收和管理所有输入

消息、系统消息,并把它们发给相应窗口的消息队列。


⑶事件驱动的程序设计

MS-DOS应用程序主要是采用顺序的、关联的、过程驱动的

程序设计方法。因此,过程的执行顺序是由程序直接控制,并

强制用户以某种不可更改的模式进行工作,交互性差。

Windows应用程序则是采用了由事件发生来控制程序运行逻

辑的设计方法,由于事件发生是随机的,没有预定的顺序,所

以用户就可以按照各种需要的、合理的顺序来安排程序的流程。

每个事件发生都会在对应的队列中放入一条消息。程序开始运

行后,总是从消息队列中读取消息等待事件的发生,并根据消

息做出相应的响应后,返回等待事件发生的状态,直至响应了

程序退出消息导致程序运行结束退出。


⑷ 支持应用程序间数据交换

Windows支持应用程序间通过以下途径进行数据交换:

① 动态数据交换 DDE(Dynamic Data Exchange),

② 剪切板(Clipboard),

③ 对象链接和嵌入 OLE(Object Linked and Embeded),

④组件对象模型 COM(Component Object Model)和分布式组件对象模型 DCOM(Distributed Component Object Model)。


5简便的动态链接库(DLL)应用

库是为应用程序提供各种功能和资源的最重要的途径,其中

动态链接库对于支持多任务的功能和资源共享和提高内存的使

用效率更为有效。Windows 平台为动态链接库的创建、安装和调

用提供了有效的支持,使得动态链接库的应用更加简便、可

行。所以动态链接库的开发和应用成为应用程序开发的重要手

段之一。


1.2Windows应用程序的特点

1事件驱动方式的程序设计模式

这种程序是由许多完成特定功能的子流程组成,在程序启动

运行之后,没有一个固定的执行流程,而是由用户的操作的结

果(事件)确定(驱动)子流程的执行,包括程序的结束。

2窗口程序设计模式

Windows 应用程序的基本单位不是过程和函数,而是窗口。

Windows 应用程序一般都具有标准的窗口界面风格。这个具有

标准风格的程序窗口界面都是由一系列的具有标准风格的界面

对象元素(如:窗口、图标、标题栏、菜单、工具栏、滚动

条、状态栏、对话框、控件、消息框等)组合而成的,例如

下图就是一个典型 Windows 程序的窗口界面。


3面向对象的程序设计模式

Windows 应用程序符合典型的面向对象结构的程序。程序为

用户提供的所有可视操作界面在程序内部都可视为一个 Windows

对象,用户对这些可视对象的操作通过事件驱动模式触发相应

Windows 对象的可调用方法。

Windows 程序的执行过程本身就是窗口和其他对象频繁创建、

处理和消亡的过程。程序执行过程中的消息发送可以理解为一

个窗口对象向别的窗口对象请求对象服务的过程。因此,用面

向对象的方法进行 Windows 程序设计与开发是极其方便、合理

的。


为了便于设计创建具有风格一致的窗口界面,微软公司在

Win32 函数库和 C++ 类库中为创建和操作上述标准界面元素提

供了大量的函数和 C++ 类。

学习和掌握有关这些标准界面元素的描述、创建的数据结

构和程序结构、C++ 类及其关系,是创建一个 Windows 程序的重

要基础之一,对于理解这些标准界面元素封装在类库中的类的

工作原理也是十分必要的,同时也有助于创建自己特殊风格的

界面元素以适应特定程序所需要的特定风格的界面。


4 资源共享

MS-DOS是单任务操作系统,DOS应用程序在运行时独占系

统的全部资源,如显示器、内存,在程序结束时才释放资源。

Windows 是一个多任务的操作系统,同时运行的各个应用程

序必须共享系统为程序运行提供的资源。系统资源是有限的,

如果使用资源的应用程序在资源使用完毕后不释放,就会造成

系统资源的枯竭,从而导致程序的运行异常,或干扰其他程序

的运行,甚至导致死机。因此 Windows 应用程序共享资源的基

本模式如下:

·向 Windows系统请求资源;


·使用资源;

·释放资源给 Windows,以供别的程序使用。

对于内存或其他硬件设备(键盘、鼠标、计数器、屏幕、

串/并接口等) 资源,一般不允许应用程序直接管理和访问,

而由 Windows 系统控制,以便向所有的应用程序提供公平的资

源,保证不中断的运行。如果应用程序确实需要对某些硬件进

行直接访问,则应当通过 Windows提供的特定 API函数实现安全

的访问。


5 程序和资源分开

在 DOS 程序中,界面设计编码工作和功能设计编码都是在

源程序中完成的。

在 Windows 应用程序中,实现界面的可视对象(如菜单、对

话框、位图等)都被从源程序中分离出来,放在资源文件(.rc)

中,并通过资源编译器将这些资源编译后,再链接到应用程序

的可执行文件或动态链接库文件中。程序与资源分离的优点:

⑴ 降低内存需求:资源可以不随着应用程序一起全部装入内

存,只有当这些资源被使用时才被装入自己的数据段,并不

驻留在应用程序的数据段;当内存紧张时,可废弃这些资源

(从内存中退出),待使用时再次自动装入。


⑵ 便于统一管理和重复利用

⑶ 应用程序与界面有一定的独立性,有利于软件的国际化


1.3Win32 程序结构

创建 Windows程序的方法有多种,最常用的方法是选择使用

WindowsAPI函数编程或 MFC编程。其中使用 MFC是完全遵照

面向对象程序设计的编程方法,是本课程的学习重点。而使用

Windows API编程对于理解 Windows程序的特点和运行机制更为

直观,因此,对于初学者首先从 Windows API编程入手是十分

必要的。

使用 Windows API编写的典型的 Windows应用程序是由主函

数和窗口函数两个基本部分组成:


1Windows 应用程序的主函数

主函数是 Windows 应用程序的入口,相当于控制台应用程序

中的 main函数。主函数名由系统确定,不允许改变。典型的主

函数中一般包括:

⑴定义主窗口类结构变量,并初始化;

⑵使用已经初始化的窗口类结构变量注册主窗口类别;

⑶创建已经成功注册的主窗口;

⑷显示并更新主窗口;

⑸启动消息循环,不断地接收消息,并分发到相应的窗口函数

去判断处理,直至收到关闭主窗口的消息,退出消息循环、

结束程序执行。


2窗口函数

窗口函数是由用户定义,由系统调用的回调函数。它的用途

是处理窗口消息,以完成程序需求的各种特定的功能。

在 Windows应用程序的运行过程中,通常总是先创建一个主

窗口,然后根据执行功能的需要,可能会有子窗口的创建和撤

消(如用于交互操作的对话框的创建和撤消)。每个窗口都应

有一个对应的窗口函数,用于处理对应窗口消息的响应操作,

因此,在一个 Windows应用程序中通常会有多个窗口函数,但

主窗口函数只有一个,换言之,Windows应用程序至少应有一个

主窗口函数。

主窗口函数必须在创建前通过主窗口类变量进行注册,而子

窗口函数一般是在主窗口函数中被注册、创建和撤消的。


1.3.1 定义窗口类的结构

窗口是一个具有标准风格的界面对象元素,它使用一个数

据结构来描述窗口样式、窗口消息处理函数、程序句柄、图标、

光标、背景刷、菜单以及描述本窗口类型结构的名称。根据这

个数据结构内容的描述,就可以创建一个具体的窗口。这个数

据结构的类型名为 WNDCLASS,定义如下:


typedef struct _WNDCLASS {

UINT style; // 窗口格式

WNDPROC lpfnWndProc; // 窗口函数指针

int cbClsExtra;// 窗口类结构变量后额外分配的字节数

int cbWndExtra;// 窗口实例后额外分配的字节数

HANDLE hInstance; // 窗口实例句柄

HICON hIcon; // 窗口图标句柄

HCURSOR hCursor;// 鼠标位置光标句柄

HBRUSH hbrBackground; // 窗口背景画刷句柄

LPCTSTR lpszMenuName; // 窗口菜单名字串指针

LPCTSTR lpszClassName; // 窗口类名字串指针

} WNDCLASS;


定义一个窗口类别结构变量,并初始化,例如:

WNDCLASSwndclass;

wndclass.style = CS_HREDRAW|CS_VREDRAW;

wndclass.lpfnWndProc = WndProc;

wndclass.cbClsExtra = 0;

wndclass.cbWndExtra = 0;

wndclass.hInstance = hInstance;

wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackgound = (HBRUSH)GetStockObject(WHITE_BRUSH);

wndclass.lpszMenuName = “MyMenu”;

wndclass.lpszClassName = “MyWndClass”;


扩展的窗口数据结构为 WNDCLASSEX,其定义如下:

typedef struct _WNDCLASSEX

{

UINT cbSize;// 新增成员,指示结构本身长度

UINT style;

WNDPROC lpfnWndProc;

int cbClsExtra;

int cbWndExtra;

HANDLE hInstance;

HICON hIcon;

HCURSOR hCursor;

HBRUSH hbrBackground;

LPCTSTR lpszMenuName;

LPCTSTR lpszClassName;

HICON hIconSm; // 新增成员,指定窗口的小图标的句柄

} WNDCLASSEX;


1.3.2窗口类的注册与窗口创建、显示和更新

1窗口类的注册

定义一个窗口类结构变量,并初始化便确定了一个指定的窗

口。由于窗口的创建和窗口函数的回调操作都是由操作系统完

成的,因此,在该指定窗口创建之前,必须在系统中注册:通

过调用窗口类函数 RegisterClass 或 RegisterClassEx 用该窗口类

结构变量确定的窗口属性和其关联的主窗口函数。注册函数的

原型和调用实例如下:


⑴窗口类注册函数 RegisterClass

原型:

ATOM RegisterClass( CONST WNDCLASS *lpWndClass );

调用实例:

WNDCLASS wndcls;

if(!RegisterClass(&wndcls))

{

MessageBox(NULL, TEXT(“This program requires Windows!”), SzAppName, MB_ICONERROR);

return 0;

}


⑵窗口注册函数扩展版 RegisterClassEx

原型:

ATOM RegisterClassEx( CONST WNDCLASSEX *lpwcx );

调用实例:

WNDCLASSEX wndcls;

if(!RegisterClassEx(&wndcls))

{

MessageBox(NULL, TEXT(“This program requires Windows NT!”), SzAppName, MB_ICONERROR);

return 0;

}


2窗口的创建

对于已经成功注册的窗口类,便可以调用窗口创建函数

CreateWindow 或 CreateWindowEx 在屏幕上显示所注册窗口类描

述的窗口并成为 Windows 应用程序的运行界面。窗口创建函数

的原型和调用实例如下:


⑴窗口创建函数 CreateWindow

原型:

HWND CreateWindow

( LPCTSTR lpClassName, // pointer to registered class name

LPCTSTR lpWindowName, // pointer to window name

DWORD dwStyle, // window style

int x, // horizontal position of window

int y, // vertical position of window

int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

HMENU hMenu, // handle to menu

HANDLE hInstance, // handle to application instance

LPVOID lpParam// pointer to window-creation data

);


调用实例:

HWND hWnd = CreateWindow

( “MyWndClass”, // 窗口类名

“The Hello Program”,// 窗口名

WS_OVERLAPPEDWINDOW, // 重叠 WS_OVERLAPPEDWINDOW 或 // 子窗口 WS_CHILD、或弹出窗口WS_POPUP。

CW_USERDEFAULT,// 使用窗口缺省水平位置 x = 0。

CW_USERDEFAULT,// 使用窗口缺省垂直位置 y = 0。

CW_USERDEFAULT, // 使用缺省窗宽,系统根据桌面确定。

CW_USERDEFAULT,// 使用缺省窗高,系统根据桌面确定。

NULL,// 无父窗口

NULL,// 使用窗口类结构变量中指定的菜单。

hInstance,// 窗口所属应用程序的句柄。

NULL// 本窗口无须附加参数。

);


⑵窗口创建函数扩展版 CreateWindowEx

原型:

HWND CreateWindowEx

( DWORD dwStyle, // extended window style

LPCTSTR lpClassName, // pointer to registered class name

LPCTSTR lpWindowName, // pointer to window name

DWORD dwStyle, // window style

int x, // horizontal position of window

int y, // vertical position of window

int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

HMENU hMenu, // handle to menu

HANDLE hInstance, // handle to application instance

LPVOID lpParam// pointer to window-creation data

);


调用实例:

HWND hWnd = CreateWindowEx

( 0,// 无扩展窗口风格

“MyWndClass”, // 窗口类名

“The Hello Program”,// 窗口名

WS_OVERLAPPEDWINDOW, // 重叠WS_OVERLAPPEDWINDOW 或 // 子窗口WS_CHILD、或弹出窗口WS_POPUP。

CW_USERDEFAULT,// 使用窗口缺省水平位置 x = 0。

CW_USERDEFAULT,// 使用窗口缺省垂直位置 y = 0。

CW_USERDEFAULT, // 使用缺省窗宽,系统根据桌面确定。

CW_USERDEFAULT,// 使用缺省窗高,系统根据桌面确定。

NULL,// 无父窗口

NULL,// 使用窗口类结构变量中指定的菜单。

hInstance,// 窗口所属应用程序的句柄。

NULL// 本窗口无须附加参数。

);


3 窗口的显示与更新

如果调用 CreateWindow或 CreateWindowEx 函数的返回值不为

0时,表明该窗口已成功创建,即系统已为窗口分配了内存,

并返回窗口的句柄。但此时所创建的窗口还未显示,需要调用

函数 ShowWindow显示窗口,并调用函数 UpdateWindow更新窗

口的客户区。这两个函数的原型和调用如下:

BOOL ShowWindow( HWND hWnd, // handle to window int nCmdShow// show state of window );

调用实例:ShowWindow( hWnd, SW_SHOW );

其中实参 SW_SHOW表示激活由 hWnd索引的窗口,并以当前的

位置和尺寸显示窗口。


系统定义了一系列表示窗口显示状态值,祥见MSDN的有关

部分

BOOL UpdateWindow( HWND hWnd/* handle of window */ );

调用实例:UpdateWindow( hWnd );


1.3.3 启动消息循环

使所创建和显示窗口能够控制程序的运行,接收窗口消息并

做出相应的处理的关键是在 WinMain函数中启动一个消息循环。

正如在前面所述 Windows操作系统把所有驱动程序运行的事

件消息都放入应用程序消息队列,所以应用程序必须不断地从

消息队列中获取消息,并分发给相应的窗口函数,这就是消息

循环所要完成的工作,也是 Windows程序的一个重要特点。最

典型、最简单的消息循环定义如下:


MSG msg;

while (GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

其中 MSG 定义了存放在消息队列中的每条消息的数据结构,其

定义如下:


typedef struct tagMSG

{

HWND hwnd; // 接收消息的目标窗口的句柄。

UINT message; // 窗口消息类别,其一般形式为 WM_XXX,如

// WM_PAINT, WM_CREATE, WM_TIMER等。

WPARAM wParam;

LPARAM lParam; // 这两个成员为 32位消息附加参数,它们的值

// 随着消息类别的不同而有所差异,例如消息

// WM_CLOSE,wParam和 lParam均为 0;消息

// WM_LBUTTONDOWN,wParam 表示鼠标按键

// 和键盘控制按键的状态,lParam 的低16 位和高

// 16 位分别表示鼠标单击点的 x和 y坐标。

DWORD time; // 消息置于消息队列的时间。

POINT pt; // 消息发生时鼠标在窗口中的坐标位置。

} MSG;


函数 GetMessage用于从消息队列中读取消息,其原型:

BOOL GetMessage( LPMSG lpMsg, HWND hWnd,

UINT wMsgFilterMin, UINT wMsgFilterMax );

参数:

lpMsg指向用于接收消息的结构变量。

hWnd指定消息所属窗口句柄,如为 NULL 表示将检索所有窗口的消息。

wMsgFilterMin指定消息检索范围下限值,0表示检索范围无下限值。

wMsgFilterMax指定消息检索范围上限值,0 表示检索范围无上限值。


返回:

·如果检索到的消息不是 WM_QUIT消息,则返回非0;

·如果检索到的消息是 WM_QUIT消息,则返回0;

·如果发生错误,例如参数 lpMsg和/或 hWnd非法,则返回

-1。使用函数 GetLastError可以获得进一步的错误信息。

·当函数发现消息队列为空时,进程被阻塞,这时进程可交

出 CPU控制权,从而让出 CPU时间片给别的进程。


函数 TranslateMessage用于将来自键盘的命令翻译成 WM_XXX

形式的命令,该函数原型:

BOOL TranslateMessage( CONST MSG *lpMsg );

参数:

lpMsg — 指向消息结构变量。

返回:

如果消息被翻译,则返回非0。


函数 DispatchMessage用于将检索到的消息分送到相应的窗口

函数。注意,应用程序并不直接调用窗口函数,而由系统根

据消息调用相应的窗口函数,所以窗口函数称为回调函数。

该函数的原型:

LONG DispatchMessage( CONST MSG *lpMsg );

参数:

lpMsg — 指向消息结构变量。

返回:

表明相应的窗口函数的返回值,虽然该值反映了被分派消息

的响应情况,但往往被忽略。


1.3.4 窗口主函数 WinMain

综前所述,Windows 应用程序的主函数 WinMain的声明如下:

int WINAPI WinMain(

HINSTANCE hInstance, // handle to current instance

HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // pointer to command line

int nCmdShow// show state of window

);

WinMain函数的典型定义包括主窗口的定义和初始化,注册,

创建,显示和更新,启动消息循环五部分。如下所示:


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

{

static TCHAR szAppname[ ] = TEXT(“HelloWin”);

HWNDhWnd;

MSGmsg;

// 定义窗口,并初始化

WNDCLASSwndclass;

wndclass.style= CS_HREDRAW|CS_VREDRAW;

wndclass.lpfnWndProc= WndProc;

wndclass.cbClsExtra= 0;

wndclass.cbWndExtra= 0;

wndclass.hInstance= hInstance;


wndclass.hIcon= LoadIcon(NULL, IDI_APPLICATION);

wndclass.hCursor= LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackgound= (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName= NULL;

wndclass.lpszClassName= szAppName;

// 注册已经定义的窗口

if(!RegisterClass(&wndclass)) {

MessageBox(NULL, TEXT(“This program requires Windows NT!”), SzAppName, MB_ICONERROR);

return 0;

}


// 创建已经成功注册的窗口

hWnd = CreateWindow ( szAppname, TEXT(“The Hello Program”), CW_USERDEFAULT,

CW_USERDEFAULT,CW_USERDEFAULT,

CW_USERDEFAULT,

NULL,

NULL,

HInstance,

NULL);


// 显示并更新已经创建的窗口

ShowWindow(hWnd, iCmdShow);

UpdateWindow(hWnd);

// 建立消息循环

While(GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}


1.3.5 窗口函数 WndProc和消息处理

Window 应用程序的窗口函数 WndProc的原型声明如下:

LRESULT CALLBACK WndProc( HWND hWnd, UINT message,

WPARAM wParam, LPARAM lParam );

其中:

·LRESULT表示函数的返回类型,与长整型等价。该函数返回值由调用它的系统使用。

·CALLBACK表示该函数是回调函数,即该函数由系统调用。

参数:

·hWnd — 接收消息的目标窗口的句柄;

·message — 消息类别;


·wParam和 lParam — 32 位消息附加信息参数。

由于主窗口函数 WinProc在主窗口类结构变量定义时,与主

窗口关联,所以由主窗口类创建的主窗口就是应用程序的主界

面,使得消息通过主函数的消息循环,被分发给与该主窗口相

关联的窗口函数。例如,一个典型的主窗口函数 WndProc定义

如下:


LRESULT CALLBACK WndProc( HWND hwnd, UINT message,

WPARAM wParam, LPARAM lParam )

{

HDChdc; // 设备上下文句柄,用于窗口中的绘图和文本输出。

PAINTSTRUCT ps;// 用于保存绘图场境的结构变量。

RECTrect; // 用于保存矩形区域信息的结构变量。

switch( message ){

case WM_PAINT:

hdc = BeginPaint( hwnd, &ps );// 获取绘图场境。

GetClientRect( hwnd, &rect );// 获取绘图区域。

DrawText( hdc, TEXT( “Hello Windows 98!” ), -1, &rect,

DT_SINGLELINE|DT_CENTER|DT_VCENTER );

// 在窗口中央绘制一行文本。

EndPaint( hwnd, &ps );// 释放绘图场境。

return 0;


case WM_DESTROY:

PostQuitMessage(0);// 寄送退出消息。

return 0;

}

return DefWindowProc( hwnd, message, wParam, lParam );

// 窗口消息的缺省处理。

}


其中:

① 函数 BeginPaint用于获取窗口的设备上下文,并保存在

lpPaint指向的变量中,该函数原型声明如下:

HDC BeginPaint( HWND hwnd, LPPAINTSTRUCT lpPaint );

参数:

·hwnd — 设备环境所属窗口的访问句柄。

·lpPaint — 指向存放绘图信息的结构变量。


② 函数 GetClientRect 用于获取窗口客户区的大小信息并保存

lpRect指向的变量中,其原型如下:

BOOL GetClientRect( HWND hWnd, LPRECT lpRect );

参数:

·hWnd — 矩形区域所属窗口的访问句柄。

·lpRect — 存放矩形区域信息的结构变量地址。


③ 函数 DrawText通过使用由 hdc指定的绘图工具和绘图属

性,在 lpRect指定的矩形区域中输出文本“Hello, Windows

98!”,该函数原型声明如下:

int DrawText( HDC hdc, LPCTSTR lpString, int nCount,

LPRECT lpRect, UINT uFormat );

参数:

·hdc — 设备环境的访问句柄。

·lpString— 指向要绘制的文本。

·nCount — 文本的字节长度。

·lpRect — 指向绘制字符串的窗口矩形区域


·uFormat — 文本的绘制标志。

本例中调用该函数的参数 nCount = -1 表示输出文本串以

null结尾;

参数 uFormat = DT_SINGLELINE|DT_CENTER|DT_VCENTER

表示文本绘制风格为:单行输出,并在 lpRect指定的矩形

区域中水平和垂直都居中对齐。


④ 函数 EndPaint与 BeginPaint配对使用,以便在使用完 hdc

后,立刻释放设备环境资源,该函数的原型声明如下:

BOOL EndPaint( HWND hWnd, CONST PAINTSTRUCT *lpPaint );

参数:

·hWnd — 窗口访问句柄。

·lpPaint — 指向存放绘制信息的结构变量。

⑤ 函数 PostQuitMessage用于在应用程序消息队列中放入一

个 WM_QUIT消息,以便 GetMessage得到此消息时,终

止消息循环和应用程序。该函数原型声明如下:

VOID PostQuitMessage( int nExitCode/*exit code */);


⑥ 函数 DefWindowProc用于完成系统的默认处理。其原型声

明如下:

LRESULT DefWindowProc( HWND hWnd, UINT Msg,

WPARAM wParam, LPARAM lParam );

参数:

·hWnd —窗口访问句柄。

·Msg — 消息标识。

·wParam — 消息的第一附加参数。

·lParam — 消息的第二附加参数。


从上述分析可以在以下几个方面归纳窗口函数的特点:

⑴ 窗口函数的作用是处理由消息循环发送来的各类消息。

⑵ 窗口函数是回调函数,即由系统通过消息调用。

⑶ 窗口函数名可以随意定义。

⑷ 程序中的每一个窗口(响应处理某些消息)必须有一个对应

的窗口函数。

⑸ 一个程序中可以有多个窗口函数,但主窗口函数只能有一

个,该窗口函数必须通过主窗口类的定义和注册与主窗口相

关联。


1.3.6资源定义文件

在 Windows应用程序中,除了主函数和窗口函数外,通常还

需要定义应用程序中需要的资源。资源包括应用程序所能够使

用的一类预定义资源中的一个对象,例如,字符串资源、加速

键表、菜单、工具栏、对话框、位图、光标、图标、版本信息

和用户自定义资源等。

在 Windows程序设计中,全部资源都被单独分离出来放在扩

展名为 .rc的资源文件中。在编译过程中,该资源文件经资源编

译器编译后,生成 .res二进制资源文件。在程序的链接过程

中, .res将与 .obj文件一起链接成可执行文件 .exe。


1.4Win32程序开发流程

Win32 程序的开发应包括如下过程:

⑴ 定义应用程序用到的资源(产生资源描述文件)。

⑵ 定义应用程序的源程序,主要包括:

① 定义 WinMain 函数,该函数需要做以下事情:

·定义窗口类;

·注册窗口类;

·创建窗口;

·显示并更新窗口;

·启动消息循环。

② 定义窗口函数。

⑶ 编译、链接源程序和资源文件。


1.5生成一个典型 Win32 窗口程序

1 在 Visual C++ 6.0集成开发环境中

⑴ 使用 AppWizard 生成一个名为 “MyWin”的 Win32 Application

窗口程序项目

⑵查看项目的目录和相应的主要文件。

⑶查看和分析源程序中关键代码。

⑷查看和分析资源文件中定义的资源。

⑸编译、链接并运行程序。


2 在 Visual C++ .NET集成开发环境中

⑴ 使用 AppWizard 生成一个名为 “MyWin”的 Win32 Project程序

项目。

⑵查看项目的目录和相应的主要文件。

⑶查看和分析源程序中关键代码。

⑷查看和分析资源文件中定义的资源。

⑸编译、链接并运行程序。


1.6介绍 Visual C++ 集成环境

1使用 AppWizard 生成各类新项目(包括向已建项目中加入各类

文件)包括:

ATL COM AppWizard

MFC AppWizard(DLL)

MFC AppWizard(EXE)

MFC ActiveX Control Wizard

Win32 Application

Win32 Console Application

Win32 Dynamic-Link Library

Win32 Static Library


2使用 Workspace查看和编辑工程项目内容

使用 ClassView查看和编辑(增加新类、增加/删除/修改类成员

等)工程的类。

使用 ResourceView查看和编辑资源文件,这些资源包括:

·加速键(Accelerator)

·位图(Bitmap)

·光标(Cursor)

·对话框(Dialog)和对话框中安放的各类控件(Controls)

·图标(Icon)

·菜单(Menu)

·字符串表(String Table)

·工具栏(Toolbar)


使用 FileView查看和编辑组成工程的各类程序源文件,这些源文

件类包括:

·实现代码源文件(Source Files)

·定义代码头文件(Header Files)

·资源代码源文件(Resource Files)

·外部依赖文件(External Dependencies)

3编译、连接和运行:

由Build 菜单提供全部编译、连接和运行操作:

⑴编译单个代码源文件(Compile xxx.cpp Ctrl+F7)

⑵ 创建工程的可执行文件(Build xxx,exe F7)

⑶重新创建工程编译、连接过程中产生的全部文件(Rebuild

All)


  • ⑷批创建调试版本和/或发布版本的可执行文件或编译、连接

  • 的全部文件(Batch Build)

  • ⑸清除当前的编译、连接结果(Clean)

  • ⑹调试(Start Debug)调试方式包括:

  • ·断点调试运行(Go F5)遇到设置断点或错误点暂停,使

  • 用功能键 F9设置/取消单个断点,Ctrl+shift+F9 取消全部断

  • 点。

  • ·单步调试运行(Step Into…F11)

  • ·运行到当前光标处(Run to CursorCtrl+F10)

  • 4使用 MSDN 寻求帮助,查询方式:

    • 按内容查询(Contents)

    • 搜寻关键词(Search…)

    • 索引查询 (Index…)

    • 键盘映射 (Keybaod Map)


.h

.cpp

.rc

.obj

.res

.exe

5工程项目中包括的主要子目录、文件类型和工作流程:

子目录:Debug, Release, Res。注意,Res子目录不能被删

除。文件类型:dsp,dsw,h,cpp,pch,res,obj,ncb,clw

等。

注意,其中 dsp, h, cpp类型文件不能被删除;ncb, clw类型文

件只有在 MFC应用程序项目中才有。编制 windows应用程序的

工作流程如下:

文本编辑

资源编辑

C++编译

资源编译

链接


1.7 介绍 Visual C++.NET 集成环境

1 介绍 .NET

⑴.NET的概念

.NET 的目标是搭建第三代因特网平台,建立平台的运行规

则,在平台上解决网站之间的协作,最大限度地获取信息。

在 .NET 平台上,通过相关的协定,不同网站被联系在一

起,自动交流、协同工作,从而提供最全面的服务。使因

特网成为 IT产业和世界经济发展促进因素的必然方向。

为此,.NET 的核心目标就是对现有的计算模式和软件开发

方式进行有针对性的调整,使之符合第三代因特网运营的

需要,所以我们应该把 .NET 理解为一种新技术、新理念。


⑵.NET Framework

为了这种新技术、新理念,就需要为软件开发人员提供一个开发平台—— .NET Framework,使应用程序的开发更为

简单。 .NET Framework 是一个可以构造、发布并运行 Web

服务的开发环境。它代表了一种崭新的软件开发模式,与

API或 COM一样都是把系统服务以接口的形式提供给开发

人员;而不同之处在于, .NET Framework 能更好地完成代

码的重用、资源配置、多语言集成开发和安全管理等任

务,在安全性、易用性和开发效率方面都远远超过以前的

开发模式。 .NET Framework 主要包括以下一些特点:


·提供了面向对象的交叉语言类库

.NET Framework 的主要组件之一,基类库(Base Class

Library)为所有编程语言提供一致、面向对象的类集,从

而避免了开发人员由于使用不同语言的类库而必须进行

的切换,大大降低了多语言集成开发的复杂程度。

·应用程序一次运行就会永远运行

.NET Framework 通过内部跟踪每个应用程序运行所需要的

文件及其版本号,从而解决了由于安装新组件覆盖了应

用程序运行依赖的组件所引起的 “DLL灾难(DLL hell)”问题。


·一致的运行环境

运行时(Runtime)是 .NET可执行程序唯一的运行环境,

从而保证了在一个 .NET 版本下编译链接并运行的应用程

序可以运行在 .NET 的所有版本下。

·通用的类型系统

.NET Framework 定义了通用类型系统(CTS),其中定义

了 .NET 所支持的全部类型以及实例化和管理这些类型的

方法。使得不同的语言可以访问相同的类型,而无须在

每种语言支持的类型之间来回转换数据,同时运行时的

类型控制权又确保了将正确的参数个数和类型传递给指定方法。


·集成的编程语言

.NET Framework 中的所有语言必须遵守一个统一的标准,

使得用不同语言代码之间的集成比以前更加容易。

·内存和资源的自动管理

.NET平台的运行时引擎可以自动跟踪应用程序的资源使

用情况,并确保不会出现资源泄漏。

·统一的异常处理

在 .NET平台上,所有的错误处理都通过异常来报告,并

且异常处理也可以跨越语言边界,这就使得开发人员可

以从代码中除去那些复杂的对可能发生的错误的判断,

使代码的编写、阅读和维护工作大大简化。


·大大简化了应用程序的发布

在 .NET平台上,安装软件意味着直接将应用程序拷贝到

机器上即可,而卸载应用程序只要删除它们就可以了,

而不必为应用程序编写特定的安装和卸载程序。

·先进的安全机制

.NET平台的安全机制的核心是一种信任机制,即如果应

用程序是来自可信任的物理介质(如 CD-ROM)或服务器,则是可信任;否则(来自不明的 Web、因特网下载或 E-mail 附件)都会使用代码的安全机制来进行控制。


2Visual C++.NET的开发范围

Visual C++.NET 与 Visual C++ 相比较,有许多新特征,其中

最重要、最显著的特征表现在:

·集成性和易用性等各个方面都有了大幅度的提高

·开发范围发生了重要变化,包括了托管(managed)应用和

非托管(unmanaged)应用两部分。

⑴托管(managed)应用

所谓托管应用是使用 .NET Framework 的SDK 来编写托管

C++语言的应用程序,这类应用可以具有 .NET Framework 的

所有特点。


托管 C++语言是对传统 C++语言的扩充,它对基本语法没

有任何更改,而我们原来熟知的传统类型发生了很多变化。

因此,编写这类应用不但需要熟悉传统的 C++语言,还需

要熟悉托管 C++语言。需要开发托管应用的需求如下:

·需要将传统的 C++应用程序合并到 .NET Framework 中

由于托管扩展可以实现托管和非托管两种类型的代码的

无缝平滑转换,所以在应用程序中,甚至同一文件中可

以混合使用两种类型的代码。这就使得开发人员可以继续使用传统的 C++应用程序功能,为了让 .NET Framework

应用程序顺利访问这些功能,编写一个很小的、转换效

率很高的包装(wrapper)程序。


·使任何与 .NET Framework 相容的语言可以访问 C++组件

托管扩展可以编写简单的托管包装类用于对访问方(映

射)公开被访问的 C++类和方法。这就使得从任何一种

与 .NET Framework 相容的语言编写的应用程序中通过调

用托管包装类和方法实现对 C++组件的访问成为可能。

特别需要指出,托管扩展支持对非托管 DLL 的调用。

·需要从传统的 C++应用程序中访问 .NET Framework 类

使用托管扩展,可以在传统 C++应用程序中直接创建、

调用一个 .NET Framework 类,并且可以像处理普通 C++

类一样对待托管类的处理。这就使得在 .NET 平台上开

发、运行的传统 C++应用程序可以获得更多的功能。


⑵ 非托管(unmanaged)应用

非托管应用是通过 Win32SDK,在微软基类库(MFC)的基础上,利用传统的 C++ 语言来编写的标准 Win32 应用程序。

保留非托管应用开发模式的主要原因是传统的 Visual C++ 具有发展历史长、开发范围、应用程序运行效率高、开发技术成熟。在那些对软件使用效率要求高的场合,如图象处理、音频处理、文字处理等,几乎成了世界级厂商进行开发工作的首选。至少就目前软、硬件水平的发展来看,使用托管类编写的 .NET 应用程序与使用编写的应用程序的运行效率是无法相提并论的。所以,无论从保障已有投资还是保证应用程序工作效率的角度来讲,传统的 Visual C++ 开发模式和主要支持库 MFC 都仍然具有强大的生命力。


不难看出,托管应用和非托管应用各有优点,因此在实际

开发工作中,开发人员可以根据项目的实际需求,灵活选择

两类应用的使用。在某些追求运行效率和性能的情况下,采

用非托管模式开发可以获得较好的效果;而在某些需要快速

完成开发任务,特别是需要充分使用 .NET平台提供网络功能

的情况下,采用托管模式开发更为适宜;在上述两种需求兼

有的复杂情况下,采用托管和非托管的混合模式开发,使两

类代码无缝转化连接可以较好地达到预期的效果。


  • Login