第一章 第一天课程——从实践中学习
本章对 Windows 程序设计作一个简单的介绍,并给出两个短小的程序作为示例,使得读者能尽快地着手运行程序。
本章只是 Windows 9x 程序设计技术的入门介绍,但它是本书核心内容的基础。
1.1 一个简化的 C++ Windows 程序
每当我着手遍写一个新的计算机程序或者学习一种新的语言时,我总是急切地希望尽快获得一些别人的 经验。我想你们也可能象我一样,所以我写了一个 C++ Windows 小程序,程序清单1.1中是它的源代码。你 只需要花几分钟时间就可读完并运行一下这个程序。
-
MessageBox(0,"He who is ruled by mem lives in sorrow.", "He who rules mem lives in confusion.", MB_OK|MB_ICONINFORMATION); return 0;
程序清单1.1 你的第一个 Windows 程序
// Program LaoTzu.cpp #include<windows.h> // Some programs to surpress unneeded warnings #pragma warning (disable:4068) #pragma argsused int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow) {
}
程序清单 1.1 是一个完全符合 C/C++ Windows 应用程序,它符合对 Windows 程序员所做的一切约定和规 则。这些简单程序行的唯一功能是显示一个消息框,框中有一句 2500 年前的道教哲人(老子)的名言。 要运行 LAOTZU.CPP,唯一要做的事是打开你的开发环境,输入程序清单 1.1 的代码并进行编译。至此, 你可以不必真正理解代码的含义,以及为什么它能工作,暂时只需要继续按我制定的计划前进。LAOTZU.CPP 不是用来教会你 Windows 程序如何工作,而只是向你展示一个 Windows 程序可以是如此简单,如此而已。 如果你用的是 Microsoft Visual C++ 软件包,你可以用如下的方法来运行你的程序: 首先录入该程序并将它保存在 LAOTZU.CPP 中,然后从文件菜单中新建一个工程,选择工程类型为 WIN32 Application,输入你的工程文件名,然后将 LAOTZU.CPP 插入到该工程中,编译并运行它。1.2 Lao Tuz 程序的核心
Lao Tuz 程序的核心是一个 MessageBox 函数,它看起来似乎十分简单,但实际上它却有点迂回曲折,而你又必须掌握它。
语法
MessageBox 函数
int MessageBox(HWND,LPCSTR,LPCSTR,UINT);
MessageBox 函数用来创建一个窗口,它有四个参数:
第二个参数 LPCSTR 是一个指向字符串常量的长指针,该字符串是你希望显示在消息框中的正文,是消息框的主要部分。
第三个参数 LPCSTR 也是一个指向字符串常量的远指针,该字符串作为消息框的标题。
第四个参数 UINT 包括一个或多个下列标志:
// Buttons // ----------------------- #define MB_OK 0x0000 包括 okay 按钮 #define MB_OKCANCEL 0x0001 包括 okay cancel 按钮 #define MB_ABORTRETRYIGNORE 0x0002 Abort,Retry,Ignore #define MB_YESNOCANCEL 0x0003 Yes No Cancel 按钮 #define MB_YESNO 0x0004 Yes No 按钮 #define MB_RETRYCANCEL 0x0005 Retry,Cancel 按钮
// Icons #define MB_ICONHAND 0x0010 STOP图标 #define MB_ICONQUESTION 0x0020 问题标志图标 #define MB_ICONEXCLAMATION 0x0030 惊叹号标志图标 #define MB_ICONASTERISK 0x0040 星号图标 #define MB_ICONINFORMATION MB_ICONASTERISK #define MB_ICONSTOP MB_ICONHAND
// Scope and focus issues #define MB_APPLMODAL 0x0000 #define MB_SYSTEMODAL 0x0001 #define MB_TASKMODAL 0x0002
// Default button specification MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3
// WIN32 MB_SETFOREGROUND MB_DEFAULT_DESKTOP_ONLY
MessageBox 函数返回一个整数值用来指示当 MessageBox 出现在屏幕上时用户选择了哪个按钮。例如,如果用户按下 OK 钮,函数将返回 IDOK。如果用户按下 CANCEL 钮,函数将返回 IDCANCEL。下面列出函数的可能返回值:
IDABORT: 用户选择 Abort 按钮; IDCANCEL: 用户选择 Cancel 按钮; IDIGNORE: 用户选择 Ignore 按钮; IDNO: 用户选择 No 按钮; IDRETRY: 用户选择 Retry 按钮; IDYES: 用户选择 Yes 按钮;
MessageBox(0,"The astrolabe of the mysteries of God is love",
"Jalal-uddin Rumi said:",MB_OK|MB_ICONEXCLAMATION);
1.3 声 音
Windows 程序呈现在用户面前的是精心制作的用户界面,不再是简单地输出一行一行的数据了。从概念 上说 Windows 程序的目标是用图画、图形和声音向用户呈现信息。
这里有两个程序,它们说明了如何让你的计算机产生声音。如果你有 SPEAKER.DRV 文件或者多媒体功 能,就可以使用这个程序的第一个版本(程序清单 1.2)。第二个版本(程序清单 1.3)可以在任何系统中 运行,只要有内置的扬声器。你最好把两个程序都运行一下,因为你应该尽量了解 MessageBeep 过程。
程序清单 1.2 CHIMES.CPP 程序
// Program CHIMES.CPP #define STRICT #include<windows.h> #include<mmsystem.h> int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow) { sndPlaySound("CHIMES.WAV",SND_SYNC); return 0; }
Chimes 程序调用一个函数 sndPlaySound,它是 Windows 多媒体范畴中的一部分。这个函数的功能是 播放 WAV 文件。 注意第二行的 #define STRICT 指示,定义它有助于建立更强壮的程序。STRICT 类型检查给那些基于 Windows 的特殊整型量(例如 HINSTANCE)一个新的含义,可以使程序员避免粗心的错误。在第四行中包含 了 MMSYSTEM.H 文件,这个文件开头的前两个字母 MM 代表 multimedia(多媒体),MMSYSTEM.H 是 Windows 程序设计环境的一个标准部分。 sndPlaySound 函数用来播放 WAV 文件。这些文通常用 WAV 作为扩展名,它包含模拟声音的数字信息。 WAV 文件能保存语音、乐曲等任何声音。
语法 sndPlaySound函数 sndPlaySound(filename,SND_FORMAT);
sndPlaySound 函数是一个高级多媒体命令,利用它可以用很简单的方法播放 WAV 文件。它的第一个参 数 filename 是要播放的 WAV 文件名。第二个参数 SND_FORMAT 代表传送到 Windows 的简单命令,它可 以是下列常数之一:
SND_SYNC 0x0000 同步播放(默认值) SND_ASYNC 0x0001 异步播放 SND_NODEFAULT 0x0002 不用默认声音 SND_MEMORY 0x0004 第一个参数是内存文件 SND_LOOP 0x0008 循环播放直到再一次调用 sndPlaySound SND_NOSTOP 0x0010 不停止当前播放的任何声音 下面是另一个例子: sndPlaySound("Chord.wav",SND_LOOP); sndPlaySound("Guitar.wav",SND_SYNC);
程序清单 1.3 说明了 Beeper 程序不要求使用多媒体功能,也能播放多媒体文件,只要你的系统支持 这种功能。
程序清单 1.3 BEEPER.CPP 程序可以在任何 PC 系统中产生声音 // Program BEEPER.CPP #define STRICT #include<windows.h> int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow) { MessageBeep(-1); return 0; }
BEEPER.CPP 程序用你的系统中内置的扬声器产生一个短促的声音。 这个程序调用 MessageBeep 函数来产生声音,这依赖于你的系统。如果你没有多媒体声音可供系统使 用,你应该给 MessageBeep 函数传送 -1。若有的话,就可以用 ICONEXCLAMATION 作为参数。 语法 MessageBeep 函数 MessageBeep(0); MessageBeep 函数可以说是所有 Windows API 中最简单的函数调用之一,它只有一个参数,而且通常 将它置为 -1。 在默认情况下,MessageBeep 函数产生一种简单的“劈劈”音,这声音非常象你在 DOS 程序中常听到 的声音。具有多媒体功能或配备有 SPEAKER.DRV 的系统,还可以用这个函数产生其他的声音。为了达到这个 目的,可以给 MessageBeep 函数传送下列常量之一:
MB_ICONSTERISK MB_ICONEXCLAMATION MB_ICONHAND MB_ICONQUESTION MB_OK
你可以选择上面任意一个常量来产生相应的声音——只要打开 Control Panel,选择 Sound 图标就可 以进行选择。选择后再次运行 Beeper 程序你就会听到完全不同的声音了(不过要记住,这种功能只有在系 统中配备了多媒体声音的功能后才有)。 下面是使用这个函数的另一个例子。 MessageBox(MB_OK);1.4 WinMain 函数是什么
DOS 或 UNIX 环境中的传统 C/C++ 程序用 main() 函数作为程序入口点,而 Windows 程序用 WinMain 函数作为程序入口点。这两个例程从功能上看大部分是相同的。也就是说,它们两个都是某个特定程序中第一 个被调用的程序。
语法 WinMain 函数 下面是 Microsoft 中对 WinMain 的说明: int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow)
WinMain 函数有四个参数:
⒈第一个参数是一个唯一的数值或句柄。它与当前程序相关。现在你可以把 HINSTANCE 看作是整型的。 不过以后你会看到
HINSTANCE 类型要比初看之时复杂一些。
⒉第二个参数只对 16 位 Windows 是重要的。它是与这个程序的另一个实例相连的唯一句柄(如果存 在另一个实例的话)。
例如,如果启动了 CLOCK.EXE 的两个副本,这个程序的第二个副本就将第一个副本 的 HINSTANCE 作为第二参数。如果程序不存在前一
实例,这个参数就置为 NULL。而在 Windows 95 和 Windows NT 中这两个参数是没有意义的,因为每一个程序都在自己的 4GB 空间
内运行。换句话说,每个程 序都在一个虚拟世界中运行,它相信自己是这个世界的唯一居民。在 Windows 95 中,应用程序感觉不到有
其他程序在系统中运行,即使这个程序正好是同一个可执行文件的第二个实例时也是如此。这样,在 Windows 95 和 Windows NT 中第
二个参数总是置为 NULL。Microsoft 在这些平台上仍然设置这个参数仅仅是为了和 Windows 3.1 兼容而已。
⒊第一个参数是一个字符串,它包含了传给程序的任何参数。这个参数的类型 LPSTR 是 Windows 中一 种提法,表示一个指向字符串
的 32 位指针。
⒋第一个参数指明程序开始运行的状态是正常状态还是最大化状态或最小化状态。
WinMain 函数返回一个整数,不过 Windows 对这个返回值从来也不做检查。也就是说,当 WinMain 函 数结束时,你的应用程序也
就结束了。指定返回值主要是为了帮助调试,或者是为了使程序更易阅读。所以,无 论 WinMain 返回值为 TRUE 还是 FALSE 都没有什么
实际意义。
1.5 Windows API
要了解 Windows API 是什么,首先要了解 Windows 是什么。那么 Windows 到底是什么呢?
许多人觉得 DOS 和 Windows 3.X 两个都不是真正的操作系统,因为因为它们没有提供真正的抢占式多 任务或任何健壮的保护方式来
对抗病态的程序所导致的错误。Windows NT 提供了所有这些功能甚至更多。在 很大程度上 Windows 9X 也是如此。
简单的说,Windows 这个词是相当一般的,没有任何特地所指的含义。事实上,Windows NT 是操作系 统,Windows 9X 看起来是
操作系统,而 Windows3.X 是操作环境,它们通过一个共同的 API(或称应用程 序设计接口)联系在一起。
API 是一组例程,既可以用来控制整个计算机,也可以用来控制计算机的某个特定的功能,例如调制解调 器、显示卡或鼠标。例如,你有
一组三个例程用来提供程序与鼠标的接口。我把这三个例程叫做 InintializeMouse,SetNousePosition 和 GetMousePosition.
这三个例程可以代表程序与鼠标间的简单的 API,可以让你启动鼠标、把鼠标放到一个指定的位置以及得 到鼠标光标的当前位置。这些简
单的功能足以形成你的应用程序和硬件的一种部件(如鼠标)之间的接口。
Windows API 其实除了类似的一组例程外没有任何其他东西,这些例程形成了应用程序和整个计算机的 接口。Windows 实际上只是一
个 API,但它同时是功能非常强的 API。
1.6 疑 难 解 答
- WINDOWS.H 是什么?
WINDOWS.H 是一个包含文件或一系列包含文件。它包含许多重要的常量、函数、结构和宏。用 C 或 C++ 编写的 Windows 程序都
要用到这些信息。在某种程度上,它是 Windows 程序设计环境中可用的最精确定义。
- 什么是一个程序的入口点?
程序的入口点是指程序开始的地方。当你单击一个图标并启动一个程序时,Windows 就把这个程序加载 到内存,并调用它的
WinMain 函数。这与任何时候在 DOS 启动程序是一样的,最大的不同是 WinMain 需 要的参数比 main() 函数多。
- 是否有必要将 WINDOWS.H 复合体从头到尾读一下?
把 WINDOWS.H 以及相关文件全部读一遍可能有点极端。不过,如果你有兴趣的话,经常花点时间去读一 下 WINDOWS.H 的几个
不同的模块却是一个好办法。
- WinExec 函数做什么?
WinExec 函数用来启动一个 Windows 或 DOS 应用程序。
- 什么是 GUI?
GUI 是指图形用户接口。一般说来,这些界面有鼠标支持和熟悉的图形功能,例如窗口、滚动栏、按钮 和对话框等等。大多数 GUI 都
设计成无论哪个应用程序运行时,呈现在用户面前的是相对统一的界面。
- 什么是 PIF?
PIF 是用来存放信息的地方,这些信息是 Windows 在运行一个 DOS 会话时要用到的。例如,用户可以 利用 PIF 来指定一个 DOS
会话是使用全屏幕还是窗口化的,或者指定 Windows 应分配给一个 DOS 会话的 存储空间的大小。