5.99k likes | 6.22k Views
( 第一讲 ). 吉林大学远程教育课件. Windows A P I 编 程. 学 时: 48. 主讲人 : 翟慧杰. 前 言. Windows 操作系统是当今世界上最为流行的操作系统之一,几乎每个台式机用户都使用过 Windows ,用过 Windows 平台下的应用软件。计算机业界对基于 Windows 应用软件的巨大需求刺激了 Windows 本身不断更新,同时也刺激了开发 Windows 应用软件的程序员队伍不断壮大。 . 前 言 ( 二 ).
E N D
(第一讲) 吉林大学远程教育课件 Windows A P I编 程 学 时:48 主讲人 : 翟慧杰
前言 Windows操作系统是当今世界上最为流行的操作系统之一,几乎每个台式机用户都使用过Windows,用过Windows平台下的应用软件。计算机业界对基于Windows应用软件的巨大需求刺激了Windows本身不断更新,同时也刺激了开发Windows应用软件的程序员队伍不断壮大。
前言(二) • Windows操作系统同时也是一个大的函数库,其中包含了数以千计的函数,这些函数能完成各种各样的功能,包括窗口管理、图形设备接口、媒体控制、内存管理和系统扩展,这些函数都被封装在一个接口中,这个接口就是Windows应用程序编程接口(Windows Application Progrmming Interface),简称Windows API。可以说,用户需要实现的所有功能通过Windows API都能实现。而且, Windows API作为操作系统不可分割的一部分,很好地体现了 Windows操作系统的特色。并且基于 Windows API的应用程序具有和 Windows操作系统的良好整合性,从而大大提高了软件的运行性能。
前言(三) • Windows API也是认识Windows操作系统的一面镜子,通过它,用户可以深入系统地掌握Windows的方方面面。很难想象,一个程序员在没有掌握Windows API的情况下,能编制出很好的基于Windows的应用程序来。 • 本课件较为全面地介绍了Windows API的编程结构及其在各个方面的具体应用,分析了大量的Windows API数据结构和函数,并结合实际应用,精心安排了大量的程序实例。读者不仅可以通过函数的分析说明来认识 Windows API,而且还可以通过编程实践来认识Windows API。书中所有的例子都在Windows 98\2000系统平台、 Visual C++ 6.0编译环境中经过了严格的测试。
第一章 Windows程序设计概述 • Windows和Windows应用程序最大的优点就是用户界面的一致性。因为大多数用户都在不同的软件包上工作,而每一个软件包都有自己的用户界面,所以用户界面一致性的设计就显得非常重要。Windows环境中的每一个窗口都包含了相同的基本特性,由于这些基本特性在应用程序中是一致的,因而用户能很容易地适应新的应用程序。 • 对用户的另一个好处是使用了基于图形的图标来代表应用程序和数据。由于Windows是一个图形用户界面,因而采用了图形图像来代表诸如文件、应用程序、窗口和目录等物理数据结构。用户可以通过鼠标的单击、双击及拖动等操作来管理这些物理结构。例如,对用户来说,双击一个代表某一应用程序的图标比起在DOS中进入相应的目录,并键人相应的应用程序名来使之执行要容易得多。
第一章Windows程序设计概述(二) • Windows操作系统提供的“所见即所得”也是一个非常友好的特点。Windows可以把文本文件等看作一串图形,而在屏幕上画出的图形,在打印的时候也将以同样形式出现。 • Windows操作系统还提供了多任务功能。多任务对于用户是很重要的,因为它允许同时运行几个应用程序,用户无需退出一个应用程序再进入另一个应用程序,而只需简单地在各个应用程序之间来回切换就可以了。 Windows对于程序员的优越性: Windows中许多对用户的优越性也是对程序员的优越性,例如一致的用户界面。因为如果应用程序的界面对每一个应用程序都是相同的,程序员就可以建立一套基本的界面设计工具,用它们来生成应用程序的用户界面(这就是可视化编程的思想):而在解决了程序的用户界面后,程序员可以花更多的时间去进行应用程序的功能设计。Windows图形用户界面的基本元素也是其具有的另一个优点。程序员可以很容易地设计出代表诸如文件和目录物理结构的图形表示,并能提供这些元素所具有的特性。
第一章Windows程序设计概述(三) • Windows对存储器的管理也是一个很明显的优点。这个优点可以使应用程序能够存取更多的内存资源,同时能保持系统设计中的灵活性。 • Windows提供了开发独立于设备的图形的能力。一个设计良好的Windows应用程序并不对硬件直接存取图形,这意味着程序并不依赖于某一确定的系统配置,所以,每一个程序员不需要为所有可能的显示器、适配卡和打印机开发设备驱动程序。
第一节Windows发展历程 Microsoft Windows是一个应用于微型计算机上的基于图形用户界面的操作系统。它为应用程序提供了一个由一致的图形用户界面构成的多任务环境。由于应用程序之间的界面是一致的,因而对于用户来说,Windows应用程序相对于基于DOS的应用程序更容易学习和使用。多任务Windows系统的好处在于允许同时运行几个应用程序,特别是运行几个专为Windows环境而创建的应用程序。当然,Windows也提供了运行非Windows的MS-DOS应用程序的机制。 Microsoft公司早在1983年就开始了Windows操作系统第一版的研制工作,并于1985年发布了Windows的1.l版。该版本的Windows支持自动的拼接式应用程序窗口及弹出式窗口。Windows经过不断升级换代,现在最新版本是Windows 2000,它基于网络环境。本课件将结合Windows 98系统平台,全面介绍Windows API程序设计。
第二节Windows 程序工作原理 Windows程序设计是一种完全不同于传统的DOS方式的程序设计方法,它是一种事件驱动方式的程序设计模式。在程序提供给用户的界面中有许多可操作的可视对象。用户从所有可能的操作中任意选择,被选择的操作会产生某些特定的事件,这些事件发生后的结果是向程序中的某些对象发出消息,然后这些对象调用相应的消息处理函数来完成特定的操作。Windows应用程序最大的特点就是程序没有固定的流程,而只是针对某个事件的处理有特定的子流程,Windows应用程序就是由许多这样的子流程构成的。 从上面的讨论中可以看出,Windows应用程序在本质上是面向对象的。程序提供给用户界面的可视对象在程序的内部一般也是一个对象,用户对可视对象的操作通过事件驱动模式触发相应对象的可用方法。程序的运行过程就是用户的外部操作不断产生事件,这些事件又被相应的对象的过程处理。 一般的程序流程如图l-l所示。
一、理解Windows消息机制 • Wndows系统是通过消息驱动的。消息的组成如下: • 消息名称(UINT)和两个参数(WPARAM,LPARAM)。
当用户进行了输入或是窗口的状态发生改变时,系统都会发送消息到某一个窗口。例如当选中某菜单命令之后会发送WM_COMMAND消息,WPARAM的高字(HIWORD(wParam)中是命令的ID号,对菜单来讲就是菜单ID。当然用户也可以定义自己的消息名称,并利用自定义消息来发送通知和传送数据。当用户进行了输入或是窗口的状态发生改变时,系统都会发送消息到某一个窗口。例如当选中某菜单命令之后会发送WM_COMMAND消息,WPARAM的高字(HIWORD(wParam)中是命令的ID号,对菜单来讲就是菜单ID。当然用户也可以定义自己的消息名称,并利用自定义消息来发送通知和传送数据。 • 一个消息必须由一个窗口接收。在窗口的过程( WNDPROC)中可以对消息进行分析,对自己感兴趣的消息进行处理。例如你希望对菜单选择进行处理,那么你可以定义对WM_ COMMAND消息进行处理的代码;如果希望在窗口中进行图形输出,就必须对WM_PAINT消息进行处理。 • Microsoft为窗口编写了默认的窗口过程,该过程负责处理那些你不处理的消息。正因为有了这个默认窗口过程,我们才可以利用Windows的窗口进行开发而不必过多关注窗口各种消息的处理。例如窗口在被拖动时会发送很多消息,而我们都可以不予理睬,让系统自己去处理。 • 说到消息就不能不说窗口句柄。系统通过窗口句柄在整个系统中唯一标识一个窗口,发送一个消息时必须指定一个窗口句柄,表明该消息由那个窗口接收。每个窗口都有自己的窗口过程,所以用户的输入就会被正确地处理。
下面有一段伪代码演示如何在窗口过程中处理消息:下面有一段伪代码演示如何在窗口过程中处理消息: • LONG WndProc(HWND hwnd , UINT MessageType ,WPARAM , LPARAM) • { • switch(MessageType) • {// 使用SWITCH语句将各种消息分开 • case(WM_PAINT): • doYourWindow (…); // 在窗口需要重新绘制时进行输出 • break ; • case (WM_LBUTTONDOWN): • doYourWork(…)// 在鼠标左键被按下时进行处理 • break ; • default: • callDefaultWndProc(…) ; // 对于其他情况就让系统自己处理 • break; • } • }
系统将维护一个或多个消息队列,所有产生的消息都会被放入或是插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄将该消息发送给拥有该窗口的程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统,所以Windows可以同时进行多个任务。下面的伪代码演示了消息循环的用法:系统将维护一个或多个消息队列,所有产生的消息都会被放入或是插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄将该消息发送给拥有该窗口的程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统,所以Windows可以同时进行多个任务。下面的伪代码演示了消息循环的用法: While(1) { id=getMessage(…); if(id==quit) break; translateMessage(…); } 当该程序没有消息通知时,getMessage就不会返回,也就不会占用系统的CPU时间。
二、协同式多任务与抢先式多任务系统 • 在16位的操作系统中只有一个消息队列,所以系统必须等待当前任务处理消息后才可以发送下一消息到相应程序,如果一个程序陷入死循环或是操作超时,系统就得不到控制权。这种多任务系统被称为协同式多任务系统。 Windows 3.x就是这种系统。 • 而32位的系统中每一个运行的程序都会有一个消息队列,所以系统可以在多个消息队列中转换,而不必等待当前程序完成消息处理才可以得到控制权。这种多任务系统就是所谓的抢先式多任务系统。Wndows 2000系列以及以前的Wndows 95/98/NT都属于这种系统。
(第二讲) 吉林大学远程教育课件 Windows A P I编 程 学 时:48 主讲人 : 翟慧杰
第三节 Windows应用程序设计特点 • 在过去,进行Windows程序设计是一件痛苦异常的事情,原因是那时候还没有现在这些设计精美的应用程序开发工具。在今天,即便是一个对Windows程序内部运行机制几乎一无所知的初学者,只需要通过不到一天的学习,也可以使用如 Visual Basic之类的程序开发工具创建出功能完整的Windows应用程序来。从某种角度说,Windows程序不是编出来的,而是由程序员画出来的。但是要知道,一个出色的Windows的应用程序并不仅在于在屏幕上绘出程序的各个窗口和在窗口中恰当地安排每一个控件。对于具有一定基础的程序员而言,更重要的内容在于知道Windows和Windows应用程序的运行机制,以及它们之间以何种方式来进行通信。换句话说,我们需要透过Windows漂亮的图形用户界面,认清在底层所发生的每一件事情。
Windows应用程序是事件驱动(或称作消息驱动)的应用程序。而 Windows 是一个多任务的操作系统,也就是说,在同一时刻,在Windows中有着多个应用程序的实例正在运行,例如我在打开字处理软件Notepad键人纯文本的同时,还打开了Winmap来播放歌曲。在这样的一个操作系统中,不可能像过去的DOS那样,由一个应用程序来享用所有的系统资源,这些资源是由Windows统一管理的。那么,特定的应用程序如何获得用户输入的信息呢?事实上,Windows时刻监视着用户的一举一动,并分析用户的动作与哪一个应用程序相关,然后,将用户的动作以消息的形式发送给该应用程序,应用程序时刻等待着消息的到来,一旦发现它的消息队列中有未处理的消息,就获取并分析该消息,最后,应用程序根据消息所包含的内容采取适当的动作来响应用户所作的操作。 • Windows为每一个应用程序,确切地说是每一个线程维护相应的消息队列。应用程序的任务就是不停地从它的消息队列中获取消息,分析和处理消息,直到接到一条叫作WM_QUIT消息为止,这个过程通常是由一种叫作消息循环的程序结构来实现的。
Windows能向应用程序发送的消息多达数百种,但是,对于一般的应用程序来说,只是其中的一部分有意义,如果你的应用程序只使用鼠标,那么如 WM_KEYUP、WM_KEYDOWN和WM_CHAR等消息就没有任何意义,也就是说,应用程序中事实上不需要处理这些事件,对于这些事件,只需要交给Windows作默认的处理即可。因此,在应用程序中,我们需要处理的事件只是所有事件中的一小部分。 事件驱动围绕着消息的产生与处理展开,一条消息是关于发生的事件的消息。事件驱动是靠消息循环机制来实现的。
Windows 应用程序的消息来源有以下 4种: (l)输入消息:包括键盘和鼠标的输入。这一类消息首先放在系统消息队列中,然后由Windows将它们送入应用程序消息队列中,由应用程序来处理消息。 (2)控制消息:用来与Windows的控制对象,如列表框、按钮、检查框等进行双向通信。当用户在列表框中改动当前选择或改变了检查框的状态时发出此类消息。这类消息一般不经过应用程序消息队列,而是直接发送到控制对象上去。 (3)系统消息:对程序化的事件或系统时钟中断作出反应。一些系统消息,像DDE消息(动态数据交换消息)要通过Windows的系统消息队列,而有的则不通过系统消息队列而直接送入应用程序的消息队列,如创建窗口消息。 (4)用户消息:这是程序员自己定义并在应用程序中主动发出的,一般由应用程序的某一部分内部处理。 Windows程序在处理消息时使用了一种叫作回调函数(Callback Function)的特殊函数。 回调函数由应用程序定义,但是,在应用程序中并没有调用回调函数的代码,回调函数是供操作系统或者其子系统调用的,这种调用通常在某一事件发生,或者在窗口或字体被枚举时。典型的回调函数有窗口过程、对话框过程和钩子函数。
第四节Windows API和SDK • 一、Windows API • 说到 Windows编程,就不能不谈到 Windows API(Windows Application Programming Interface,Windows应用程序编程接口),它是所有 Windows应用程序的根本之所在。简单地说,API就是一系列的例程,应用程序通过调用这些例程来请求操作系统完成一些低级服务。在Windows这样的图形用户界面中,应用程序的窗口、图标、菜单和对话框等就是由API来管理和维护的。在DOS应用程序下,可以通过getchar ()等函数直接等待键盘输入,并直接向屏幕输出。而在Windows下,由于允许多个任务同时运行,应用程序的输入输出是由Windows来统一管理的。 Windows 操作系统包括 3个内核基本元件: GDI,KERNEL,USER。其中 GDI(图形设备接口)负责在屏幕上绘制像素、打印硬拷贝输出、绘制用户界面(包括窗口、菜单。对话框)等。系统内核KERNEL支持与操作系统密切相关的功能:如进程加载,文本切换、文件I/O,以及内存管理、线程管理等。USER为所有的用户界面对象提供支持,它用于接收和管理所有输入消息、系统消息并把它们发给相应的窗口的消息队列。消息队列是一个系统定义的内存块,用于临时存储消息;或是把消息直接发给窗口过程。每个窗口维护自己的消息队列,并从中取出消息,利用窗口函数进行处理。
Windows API具有两种基本类型: Winl6 API和Win32 API。两者在很多方面非常相像,但是Win32 API除了几乎包括了Winl6 API中的所有内容以外,还包括很多的其他内容。Windows API依靠 3个主要的核心组件提供 Windows的大部分函数,在Winl6和 Win32中,它们具有不同的名称,如表l一1所示。 • 虽然Winl6 API组件带有.EXE的扩展名,但是它们事实上都是动态链接库(.DLL),不能单独运行。其他一些非核心的Windows API由其他组件所提供的DLL来实现,这些组件包括通用对话框、打印、文件压缩、版本控制以及多媒体支持等。
二、Windows SDK • Windows SDK(Windows Software Development Kit, Windows软件开发工具包)和Windows API紧密联系,它是一套帮助 C语言程序员创建Windows应用程序的工具,在Windows SDK中包括了以下几个组成部分: • .大量的在线帮助,这些帮助描述了Windows编程所可能用到的函数、消息、结构、宏及其他资源。 • .各种编程工具,如对话框编辑器及图像编辑器等。 • .Windows库及头文件。 • .使用C语言编写的示例程序。 • 该工具包的最新版本就是我们正在使用的 Win32 SDK。在安装 Visual C++的同时,Win32 SDK也安装到你的计算机上了。尽管MFC提供了对Win32 API的比较完整的封装,但是,在某些情况下,我们更倾向于直接调用 Win32 API,因为这有时候可以获得更高的效率,并且有着更大的自由度。而且,使用MFC编写的新风格的Windows应用程序的工作方式基本上与使用SDK编写的程序一样,它们往往有着很多的共同之处,只是使用MFC更加方便,因为它隐藏了大量的复杂性。 • 面向对象的编程方式是当前最流行的程序设计方法,但是, Win32 API本身却是基于C语言的过程式编程的,SDK和MFC的最主要的不同之处也就是C与C+十之间的差别,使用MFC进行Windows应用程序设计需要面向对象的编程思想和方法,
三、使用 SDK编写 Windows应用程序 • 传统的DOS程序以main() 函数作为进入程序的初始人口点,在Windows应用程序中,main函数被WinMain函数取而代之,WinMain函数的原型如下: • int WINAPI WinMain(HINSTANCE hInstance, // 当前实例句柄; • HINSTANCE hPrevInstance, // 前一实例句柄; • LPSTR lpCmdLine, // 指向命令行参数的指针; • int nCmdshow) // 窗口的显示状态。 • 所谓的句柄是一个标识对象的变量,或者是一个对操作系统资源的间接引用。句柄是指向一个“无类型对象”(Void*)的指针,也就是一个4字节长的数据。其声明是这样的: typedef void *HANDLE • Windows是一个多任务操作系统,它可以同时运行多个程序或一个程序的多个副本。为了对同一程序的多个副本进行管理,Windows引入了实例句柄。Windows为每个应用程序建立一张表,实例句柄就好像是这张表的一个索引。Windows不仅使用句柄来管理实例,也用它来管理窗口、位图、字体、元文件、图标等系统资源。 • 一般情况下,我们应该在WinMain函数中完成下面的操作: • (l)注册窗口类。 • (2)创建应用程序主窗口。 • (3)进入应用程序消息循环。
第五节Windows API程序设计基础知识 • 要在Windows上开发应用程序,就必须对Windows程序设计有所了解。 • 在编程时,变量、函数的命名是一个极其重要的问题。好的命名方法使变量易于记忆且程序可读性大大提高。 Microsoft采用匈牙利命名法来命名 Windows API函数和变量。匈牙利命名法是由Microsoft的著名开发人员,Excel的主要设计者查尔斯·西蒙尼在他的博士论文中提出来的,由于西蒙尼的国籍是匈牙利,所以这种命名法叫匈牙利命名法。 • 匈牙利命名法为C标识符的命名定义了一种非常标准化的方式,这种命名方式是以下面两条规则为基础的: • .标识符的名字以一个或者多个小写字母开头,用这些字母来指定数据类型。表l-2列出了常用的数据类型的标准前缀。 • .在标识符内,前缀以后就是一个或者多个第一个字母大写的单词,这些单词清楚地指出了源代码内那个对象的用途。比如,m_szStudentName表示一个学生名字的类成员变量,数据类型是字符串型
第六节Windows API程序的开发步骤 • 开发基于 Windows API的应用程序一般可以参考下面的步骤。启动Visual C++编译器后,选择 File菜单下的 New菜单项,在列表框中选择 Win32 Application项。然后在 Project name文本框中输入新建的项目名,再在Location文本框中输入新建项目所在的路径。 可用如下三种方式的任意一种创建 Windows API应用程序。 • . An empty project一个空项目; • . A simpla Win32 application 一个简单的Win32应用程序 • . A typical" Hello World”application一个典型的“Hello Word”应用程序。 • 如果选择A simple Win32 application项, Visual C++ 编译器将自动为用户生成项目的框架文件。 • 对初学 Windows API程序设计的程序员来说,“Hello World!”应用程序是一个很好的范例。当选择了 A typical "Hello World!”application这一项后,Visual C++编译器会自动创建一个完整的应用程序,这个程序将在弹出的窗口中显示 Hello World!。 如果希望直接键人源程序,可以选择 An empty project这一项。 • 项目框架建立起来后,需要向其中加入相关的文件,这些文件可能是已经存在的,也可能是需要重新编辑建立的。 • 要向项目中加入文件,可以单击Visual C++编译器中Project菜单下的Add To Project级联菜单下的Files菜单项, 选择需要插入到项目中的文件,然后单击OK按钮,相应的文件就会被插入到项目中。单击Visual C++编译器中File菜单下的New菜单项,列表框中列出了所有可以新建的文件类型,这些文件中用得最为频繁的是C++ Source File。首先选中列表框中的C++ Source File项,然后在File文本框中键入文件名,再单击OK按钮。这样就可以在Visual C++编译器中键人相应的源文件了。其他类型的文件可以参照C++ Source File的建立步骤。
第二章WINDOWS应用程序框架 创建窗口只需调用CreateWindow 函数即可,但实际上并不这么简单。但实际上并非真得这么简单。虽然创建窗口的函数确实被命名为CreateWindow,但您将发现CreateWindow的第一个参数就是所谓的“窗口类名称”,并且该窗口类连接所谓的“窗口过程”。在我们调用CreateWindow之前,有一点背景知识会对您大有帮助。
第一节 WINDOWS应用程序的基本结构 • 进行Windows程序设计,实际上是在进行一种面向对象的程序设计(OOP)。这一点在Windows中使用得最多的对象身上表现最为明显。 • 这种对象正是Windows之所以命名 为“Windows”的原因,它具有人格化的特征,这就是那个叫做“窗口”的对象。
一、总体结构 桌面上最明显的窗口就是应用程序窗口。这些窗口含有显示程序名称的标题栏、菜单,甚至可能还有工具栏和滚动条。另一类窗口是对话框,它可以有标题栏也可以没有标题栏。 装饰对话框表面的还有各式各样的按钮、单选钮、复选框、列表框、滚动条和文本输入区域。其中每一个小的可视对象都是一个窗口。更确切地说,这些都称为“子窗口”或“控件窗口”或“子窗口控件”。
作为对象,用户会在屏幕上看到这些窗口, 并通过键盘和鼠标直接与它们进行交互操 作。更有趣的是,程序员的观点与用户的 观点极其类似。窗口以“消息”的形式接收 窗口的输入,窗口也用消息与其他窗口通 讯。对消息的理解将是学习如何编写 Windows程序必须逾越的障碍之一。 这有一个windows的消息示例:我们知道,大多数的windows程序都有大小合适的应用程序窗口。也就是说,您能够通过鼠标拖动窗口的边框来改变窗口的大小。通常,程序将通过改变窗口中的内容来响应这种大小的变化。是Windows本身而不是应用程序正在处理与用户重新调整窗口大小相关的全部代码。由于应用程序能改变其显示的格式,所以它也“知道”窗口大小改变了。
应用程序是如何知道用户改变了窗口的大小的呢?应用程序是如何知道用户改变了窗口的大小的呢? 由于程序员习惯了常规的字符模式程序,操作系统 没有设置将此类消息通知给用户的机制。问题的关 键在于理解Windows所使用的体系结构。当用户改 变窗口的大小时,Windows给程序发送一条消息指 出新窗口的大小。然后程序就可以调整窗口中的内 容,以反映大小的变化。 “Windows给程序发送消息。”我们希望读者不要对这句话视而不见。它到底表达了什么意思呢?我们在这里讨论的是程序代码,而不是一个电子邮件系统。操作系统怎么给程序发送消息呢?其实,所谓“Windows给程序发送消息”,是指Windows调用程序中的一个函数,该函数的参数描述了这个特定消息。这种位于Windows程序中的函数被称为“窗口过程”。 无疑,读者对程序调用操作系统的思路是很熟悉的。例如,程序在打开磁盘文件时就要使用有关的系统调用。读者所不习惯的,可能是操作系统调用程序,而这正是Windows面向对象体系结构的基础。
程序创建的每一个窗口都有相关的窗口过程。这个程序创建的每一个窗口都有相关的窗口过程。这个 窗口过程是一个函数,既可以在程序中,也可以 在动态链接库中。Windows通过调用窗口过程来 给窗口发送消息。窗口过程根据此消息进行处理, 然后将控制返回给Windows。 更确切地说,窗口 通常是在“窗口类”的基础上创建的。窗口类标识 了处理窗口消息的窗口过程。使用窗口类使多个窗口能够基于同 一个窗口类,并且使用同一个窗口过程。例如,所有Windows程序中的所有按钮均基于同一个窗口类。这个窗口类与一个位于Windows动态链接库(处理所有的按钮窗口消息)的窗口过程相联系。在面向对象的程序设计中,对象是代码与数据的组合。窗口是一种对象,其代码是窗口过程。数据是窗口过程保存的信息,以及Windows为每个窗口系统中那个窗口类保存的信息。窗口过程处理给窗口发送的消息。这些消息经常是告知窗口,用户正使用键盘或鼠标进行输入。这正是按钮窗口知道它被“按下”的奥妙所在。在窗口大小改变或窗口表面需要重画时,由其他消息通知窗口。
Windows程序开始执行后,Windows为该 程序创建一个“消息队列”。这个消息队列 用来存放该程序可能创建的各种不同窗口 的消息。程序中有一小段代码,叫做“消息 循环”,用来从队列中取出消息,并且将它 们发送给相应的窗口过程。有些消息直接发送给窗口过程,不用放人消息队列中。如果您对这段有关Windows体系结构的过于简略的描述将信将疑,就让我们去看看在实际的程序中,窗口、窗口类、窗口过程、消息队列、消息循环和窗口消息是如何相互配合的。这或许对您会有些帮助。创建一个窗口首先需要注册一个窗口类,那需要一个窗口过程来处理窗口消息。这包 括一段几乎出现在每一个Window程序中的固定代码。程序2.1所示的HELLOWIN程序就是一个包括这段固定代码的简单程序。
第三讲) 吉林大学远程教育课件 Windows A P I编 程 (第三讲) 学 时:48 主讲人 : 翟慧杰
二、程序2.1 #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("HelloWin") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; 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.hbrBackground = (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, // window class name TEXT ("The Hello Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; }
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE: PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ; return 0 ; 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) ; }
三、总体考虑 实际上,每一个Windows程序代码中都包括 HELLOWIN.C程序的大部分。没人能真正记 住编写此代码的全部语法;通常,Windows程 序员在开始一个新程序时总是复制一个 现有的 程序,然后再做相应的修改。上面提到, HELLOWIN将在其窗口的中央显示文本串。 那不完全正确。文本实际显示在程序客户区的中央,它在图中是标题栏和边界范围内的大片白色区域。这种特性对我们很重要;客户区就是程序自由绘图并且向用户交付可观输出的窗口区域。如果您认真思考一下,将会发现虽然只有80行代码,这个窗口却令人惊讶地具有许多功能。您可以用鼠标按住标题栏,在屏幕上移动窗口;可以按住边框,改变窗口的大小。在窗口大小改变时,程序自动地将“hello,windows 98!”文本串重新定位在客户区域的中央。您可以按最大化按钮,放大HELLOWIN以充满整个屏幕;也可以按最小化按钮,将程序压缩为一个图标。
您可以从系统菜单(标题栏最左端的小图际)中您可以从系统菜单(标题栏最左端的小图际)中 执行所有选项;也可以通过从系统莱单中选择 Close选项,或者通过单未标题栏最右端的关闭 按钮,或者通过双击标题栏最左端的图标来关闭 窗口以终止程序的执行。 当然,我们首先要从整体上看一下。HELLOWIN.C也有一个WinMain函数,但它还有另外一个函数,名为WndProc。这就是窗口过程。注意,在HELLOWIN,C中没有调用 WndProc的代码。然而,在 WinMain中有对WndProc的声明,这就是函数要在程序顶部附近声明的原因。
1. Windows 函数调用 HELLOWIN至少调用了18个Windows函数。下面以它们在 HELLOWIN,C中出现的次序列出这些函数以及各自的简明描述: LoadIcon 加载图标供程序使用; LodCursor 加载鼠标指针供程序使用; GetStockObject 获取一个图形对象,在这个例子中,是获取绘制窗口背景的刷子; RegisterClass为程序窗口注册窗口类; MessageBox 显示消息框; CreateWindow 根据窗口类创建一个窗口; ShowWindow 在屏幕上显示窗口; UpdateWindow 指示窗口刷新自身; GetMessage 从消息队列中获取消息; TranslateMessage转换某些键盘消息; DispatchMessage将消息发送给窗口过程; PlaySound 播放一个声音文件; BeginPaint 开始窗口绘制; GetClientRect 获取窗口客户区的尺寸; DrawText 显示文本串; EndPaint 结束窗口绘制; PostQuitMessage 在消息队列中插入一条“退出”消息; DefWindowProc 执行默认的消息处理。 这些函数均在平台SDK文档中说明,并在不同的头文件中声明,其中绝大多数在 WINUSER.H中声明。
2. 大写的标识符 大家可能注意到,在IIELIDWIN.C中有很多大写 的标识符,这些标识符都是在Windows的头文件 中定义的。其中有些标识符包含由两个字母或者 三个字母组成并后跟一个下划线的前缀: CS_HREDRAW CS_VREDRAW IDI_APPLICATION IDC_ARROW MB_ICONERROR WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 这些都是简单的数值常量。前缀指示该常量所属的类别,如下页 表所示。
在编写Windows程序时,不用去记这些数值常量。实际上,Windows中使用的每个数值常量在头文件中均有相应的标识符定义。在编写Windows程序时,不用去记这些数值常量。实际上,Windows中使用的每个数值常量在头文件中均有相应的标识符定义。
3. 新的数据类型 HELLOWIN.C中的其他标识符是新的数据类型, 也在Window头文件中使用 typedef 语句或者 #define语句加以定义。这最初是为了便于将 Windows程序从原来的16位系统上移植到本 来的基于32位(或者其他)技术的操作系统上。 这种作法并不如当时每个人想象的那样顺利,但是这种概念基本上是正确的。 有时这些新的数据类型只是为了方便起见的一个缩写。例如,用于WinProc的第二个参数的UINT数据类型只是一个unsigned int (无符号整数),在Windows 98中,这是一个32位的值。 用于WinMain的第三个参数的PSTR数据类型是指向一个字符串的指针,即是一个char*。
其他数据类型的含义不太明显。例如,WndProc 的第三和第四个参数分别被定义WPARAM和LPARAM, 这些名字的来源有点历史背景:当Window是一种 16位系统时,WndProc的第三个参数被定义为一 个WORD,这是一个历位的无符号短整数,而第四个参数被定义为一个LONG,这是一个32位有符号的长整数,从而导致了对单词“PARAM”加前缀“W”和“L”。当然,在32位的Windows中,WPARAM被定义为一个UINT,而LPARAM被定义为一个LONG (就是C中的long数据类型),因此窗口过程的这两个参数都是 32位的值。这也许有点奇怪,因为 WORD数据类型在 Windows 98中仍然被定义为16位的无符号整数,因此“PARAM”的“W”在某种意义上是不正确的名字。 WndProc函数返回一个类型为LRESULT的值,该值简单地被定义为一个LONG。WinMain函数被指定为一个WINAPI类型(在头文件中定义的所有Windows函数都被指定这种类型),而WndProc函数被指定为一个CALLBACK类型。这两个标识符都被定义为_stdcall,指在Windows本身和用户的应用程序之间发生的函数调用的特殊调用序列。
HELLOWIN还使用了Windows头文件中定义的4种 数据结构(我们将在本章稍后加以讨论)。这些 数据结构如表所示。 表中前面两个数据结构在WinMain中使用,分别 定义了两个名为msg和wndclass的结构,后面两 个数据结构在WndProc中使用,分别定义了ps和rect结构。
4. 句柄简介 最后,还有3个大写标识符(见表),用于不同 类型的“句柄”。 句柄在Windows中使用非常频繁。 在本章结束之前,我们将遇到HICON(图标句柄)、 HCURSOR鼠标指针句柄)和HBRUSH(图形刷句 柄)。 句柄是一个数(通常为32位),它代表一个 对象。Windows中的句柄类似传统C或者MS-DOS程序设计中使 用的文件句柄。程序几乎总是通过调用Windows函数获取句柄。程序在其他Window函数中使用这个句柄,以引用它代表的对象。句柄的实际值对程序来说是无关紧要的。但是,向您的程序提供句柄的Windows模块知道如何使用它来引用相应的对象。
5.匈牙利表示法 要在Windows上开发应用程序,就必须对Windows 程序设计有所了解。 在编程时,变量、函数的命名是一个极其重要 的问题。好的命名方法使变量易于记忆且程序可 读性大大提高。 Microsoft采用匈牙利命名法来命 名 Windows API函数和变量。匈牙利命名法是由Microsoft的著名开发人员,Excel的主要设计者查尔斯·西蒙尼在他的博士论文中提出来的,由于西蒙尼的国籍是匈牙利,所以这种命名法叫匈牙利命名法。 匈牙利命名法为C标识符的命名定义了一种非常标准化的方式,这种命名方式是以下面两条规则为基础的: .标识符的名字以一个或者多个小写字母开头,用这些字母来指定数据类型。下表列出了常用的数据类型的标准前缀。 .在标识符内,前缀以后就是一个或者多个第一个字母大写的单词,这些单词清楚地指出了源代码内那个对象的用途。比如,m_szStudentName表示一个学生名字的类成员变量,数据类型是字符串型。