2.1 基于Windows的应用程序设计

读者要理解Visual C++应用程序的开发过程,需要先理解Windows程序的运行机制。由于Visual C++是基于Windows操作系统的集成开发环境,需要明白在Windows环境下编程和在其他环境下编程的一些根本性的差别。对于Windows程序运行的一些根本性的概念,是一个Visual C++程序员所必须掌握的。

Windows程序设计是一种完全不同于传统的DOS方式的程序设计方法。其内部运行原理是一种事件驱动方式的程序设计模式,主要是基于消息的。当用户需要完成某种功能时会调用操作系统的某种支持,然后操作系统将用户的需要包装成消息,并投递到消息队列中,最后应用程序从消息队列中取得消息并进行响应,其流程如图2-1所示。

图2-1 Windows程序设计

2.1.1 基于Windows的应用程序接口(API)

Windows程序设计中,应用程序要完成某个功能,通常都是以函数调用的形式实现的。同样,应用程序也通常以函数调用的方式来通知操作系统执行相应的功能。Windows操作系统将它所能够完成的功能写成函数、常量、变量等形式,并提供给应用程序使用,而应用程序开发人员无须考虑其底层的源代码及其工作机制的内部细节。应用程序对这些函数的调用就叫做系统调用。这些函数、常量、变量等的集合就是Windows操作系统提供的应用程序编程接口(Application Programming Interface),简称Windows API。

如果把进行Windows编程看作是程序设计人员在创作一幅艺术作品,比如一幅画,那么Windows API就是程序员手中的笔。通过这支笔,程序员可以画出多姿多彩的图画,如图2-2所示。

图2-2 API

例如,Windows中创建窗口的CreateWindow函数就是一个API函数,在应用程序中调用这个函数,操作系统就会按照该函数提供的参数信息产生一个相应的窗口,如下所示即调用CreateWindow函数。

CreateWindow(szWindowClass,szTitle);

此外,Windows提供了许多API函数,其函数名和参数名通常以英文单词来命名,用户通过其英文含义便可清楚地知道该API函数及其每个参数的用途。如显示窗口用ShowWindow,退出Windows操作系统用ExitWindows等。此外,API函数的正确拼写格式及各参数的详细信息都可以在MSDN(Microsoft Developer Network,微软开发者网络,专门为开发人员进行软件开发所提供的一个服务)中快速检索到。

说明

读者不要将这里的API与Java API或其他API混淆。API正如其语义一样,已成为一种被广泛使用的专业术语。如果某个系统或某个设备提供给某种应用程序,用于对其进行操作的函数、类或组件等的集合,就称为该系统的API。

2.1.2 句柄的原理

在Windows编程中开发者会经常接触到一个称为句柄(HANDLE)的概念,句柄是指使用一个四字节长的整数,用于标识应用程序中的不同对象或同类对象中的不同实例。Windows程序中产生的任何资源(要占用某一块或大或小的内存),如图标、光标、位图、窗口和应用程序的实例(已加载到内存运行中的程序)等,操作系统都要将其放入到相应的内存中,并为这些内存指定一个唯一的标识号,这个标识号即是该资源的句柄。

比如在大街上来来往往的汽车,有许多是相同型号、相同品牌的,也有许多是不同型号、大小的汽车。如何分辨某一辆汽车是属于谁的呢?就是通过车牌号,因为车牌号是唯一的,那么车牌号就是汽车这个资源的句柄,如图2-3所示。

图2-3 句柄

操作系统要管理和操作这些资源,首先都是通过句柄来找到对应的资源的。一般来说,按资源的类型,可以将句柄分成图标句柄(HICON)、光标句柄(HCURSOR)、位图句柄(HBITMAP)、窗口句柄(HWND)、应用程序实例句柄(HINSTANCE)等类型。例如,操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。以指定的标识号作为句柄来创建窗口的语句如下。

hWnd = CreateWindow(szWindowClass,szTitle)

2.1.3 Windows应用程序入口——WinMain()函数

WinMain()函数是所有Win32程序的入口函数,这个函数是应用程序的基础,类似于DOS下的main()函数。当Windows操作系统启动一个程序时,其调用的就是该程序的WinMain()函数,当WinMain()函数结束或返回时,Windows应用程序就结束了。

一般来说,WinMain()函数接受4个参数。这些参数都是系统调用WinMain()函数时,传递给应用程序的,其原型如下。

int WINAPI WinMain(
        HINSTANCE hInstance,      //当前运行的实例句柄
        HINSTANCE hPrevInstance,  //当前实例的上一个句柄
        LPSTR lpCmdLine,          //命令行指针
        int nCmdShow,             //显示窗口状态
);

各参数说明如下。

● hInstance:该应用程序当前实例的句柄。同一台计算机上可运行同一个应用程序的多个实例。每启动一个这样的实例,操作系统都要给该实例分配一个标识号,即实例句柄。随后系统调用程序中的WinMain函数,并将该实例句柄传递给参数hInstance。

● hPrevInstance:由同一个应用程序产生的先前实例的句柄。对于一个32位程序,该参数往往为NULL。

● lpCmdLine:是一个字符串,里面包含有传递给应用程序的参数串。例如,在Visual C++开发环境中给应用程序传递参数,单击【Project】|【Settings】菜单项,在弹出的【Project Settings】对话框中选择【Debug】标签,在该标签页的【Program arguments】编辑框中输入想要传递给应用程序的参数。

● nCmdShow:指定程序窗口应该如何显示,如参数SW_SHOWMAXIMIZED将激活窗口并将其最大化,SW_MINIMIZE将最小化指定的窗口,SW_HIDE将隐藏窗口,并激活另外一个窗口等。

如果WinMain在消息循环之前返回,程序没有正常运行,返回值为0。如果在消息循环之后返回,返回值为WM_QIUT消息的wParam参数。

一般来说,一个完整的Win32程序,其实现大致可分为4个步骤。例如,在Windows中创建一个窗口,并在该窗口中响应键盘及鼠标消息,其程序实现步骤如下。

1 WinMain函数的定义。

2 创建一个窗口。

3 进行消息循环。

4 完成回调函数。

说明

在Visual C++ 6.0中使用MFC进行Windows编程时,开发者可能找不到WinMain函数,这是因为MFC将WinMain函数隐藏在应用程序的框架中,编译时会自动将该函数链接到可执行文件中。开发者可以重写WinMain函数,但一般不需要这样做。

2.1.4 消息及消息队列机制

在应用程序中,用户所有的操作都是通过消息机制(Message)来传递给操作系统的。操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序。例如,用户在某个程序活动时按了一下键盘键,操作系统马上能感知到这一事件,并且能够知道用户按下的是哪一个键。操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序。由应用程序决定如何对这一事件作出反应,对事件作出反应的过程就是消息响应。

比如,有只蚊子叮了某个人一口,神经末梢(相当于操作系统)马上感知到这个事件,并传递给了大脑(相当于应用程序)。大脑最终决定如何对这一事件作出反应。如将蚊子赶走,或是将蚊子拍死。其中蚊子叮人就产生了消息,而用户的反应就是消息响应。在Windows编程中,消息与应用程序的关系如图2-4所示。

图2-4 消息与应用程序的关系

了解了消息的概念后,读者可以再来看看消息队列(Queue)的概念。Windows本身维护一个系统消息队列,对于每一个正在运行的Windows应用程序,系统会为其建立一个“消息队列”,用于存放该应用程序可能创建的各种消息。消息队列是一个先进先出的缓冲区,通常是一个某种变量类型的数组。消息队列里的每一个元素就是一条消息。操作系统将生成的每个消息按先后顺序放进消息队列里。第一条消息放入第一格,第二条消息放入第二格,依此类推。

一般来说,应用程序中包含一段消息循环代码,用于从消息队列中检索这些消息。应用程序总是取走队列中的第一条消息。消息取走后,第二条消息成为第一条,剩余的消息依次前移。应用程序取得消息后,便能够知道用户的操作和程序状态的变化,并把它们分发到相应的函数中进行处理。

例如,若应用程序从队列里取到了一条WM_CHAR消息,那一定是用户输入了一个字符,并且能够知道输入的是哪个字符。应用程序得到消息后,就要对消息进行处理,这就是通常说的消息响应。消息响应是用户通过编码实现的,这也是Windows程序的主要代码。

在消息响应代码中,很可能又要调用操作系统提供的API函数,以便完成特定的功能。如果用户收到窗口的WM_CLOSE消息,可以调用API函数DestroyWindow来关闭该窗口,或是用MessageBox函数来提示用户是否真的要关闭窗口。

因此,使用Visual C++编写Windows程序时需要掌握以下两点。

● 不同的消息所代表的用户操作和程序状态。

● 要让操作系统执行某个功能所对应的API函数。

提示

Windows编程的主要代码都集中在对消息的处理上,或者说编写消息响应(处理)函数是进行Windows编程的主要工作。