Chinaunix首页 | 论坛 | 博客
  • 博客访问: 325881
  • 博文数量: 72
  • 博客积分: 1730
  • 博客等级: 上尉
  • 技术积分: 743
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-27 18:49
文章分类

全部博文(72)

文章存档

2012年(72)

我的朋友

分类: WINDOWS

2012-08-13 14:04:12

Aug 13 2012 The 3rd chapter
Windows and  Message  窗口和信息

程序建立的每一个窗口都有相关的窗口消息处理程序。这个窗口消息处理程序是一个函数,既可以在程序中,也可以在动态链接库中。Windows通过呼叫窗口消息处理程序来给窗口发送消息。窗口消息处理程序根据此消息进行处理,然后将控制传回给Windows。

更确切地说,窗口通常是在「窗口类别」的基础上建立的。窗口类别标识了处理窗口消息的窗口消息处理程序。使用窗口类别使多个窗口能够属于同一个窗口类别,并使用同一个窗口消息处理程序。例如,所有Windows程序中的所有按钮均依据同一个窗口类别。这个窗口类别与一个处理所有按钮消息的窗口消息处理程序(位于Windows的动态链接库中)联结。

在对象导向的程序设计中,对象是程序与数据的组合。窗口是一种对象,其程序是窗口消息处理程序。数据是窗口消息处理程序保存的信息和Windows为每个窗口以及系统中那个窗口类别保存的信息。

窗口消息处理程序处理给窗口发送消息。这些消息经常是告知窗口,使用者正使用键盘或者鼠标进行输入。这正是按键窗口知道它被「按下」的奥妙所在。在窗口大小改变,或者窗口表面需要重画时,由其它消息通知窗口。

Windows程序开始执行后,Windows为该程序建立一个「消息队列」。这个消息队列用来存放该程序可能建立的各种不同窗口的消息。程序中有一小段程序代码,叫做「消息循环」,用来从队列中取出消息,并且将它们发送给相应的窗口消息处理程序。有些消息直接发送给窗口消息处理程序,不用放入消息队列中。

如果您对这段Windows架构过于简略的描述将信将疑,就让我们去看看在实际的程序中,窗口、窗口类别、窗口消息处理程序、消息队列、消息循环和窗口消息是如何相互配合的。这或许会对您有些帮助。


Aug 15 2012 the 3rd chapter Windows and  Message  窗口和信息

Windows 程序设计的难点

即使有了对HELLOWIN的说明,读者对程序的结构和原理可能仍然觉得神秘。在为传统环境编写简单的C程序时,整个程序可能包含在main函数中。而在HELLOWIN中,WinMain只包含了注册窗口类别,建立窗口,从消息队列中取出消息和发送消息所必须的程序代码。

序的所有实际动作均在窗口消息处理程序中发生(widows procedure)。在HELLOWIN中,这些动作不多,WndProc只是简单地播放了一个声音文件并在窗口中显示一个字符串。但是在后面的章节中,读者将发现,Windows程序所作的一切,都是响应发送给窗口消息处理程序的消息。这是概念上的主要难点之一,在开始写作Windows程序之前,必须先搞清楚。

别呼叫我,我会呼叫您

前面我们提到过,程序写作者已经熟悉了使用操作系统呼叫的做法。例如,C程序写作者使用fopen函数打开文件。fopen函数最终通过呼叫操作系统来打开文件,这一点问题也没有。

但是Windows不同,尽管Windows有1000个以上的函数可供程序呼叫,但Windows也呼叫使用者程序,比如前面定义的窗口消息处理程序WndProc。窗口消息处理程序与窗口类别相关,窗口类别是程序呼叫RegisterClass注册的。依据该类别建立的窗口使用这个窗口消息处理程序来处理窗口的所有消息。Windows通过呼叫窗口消息处理程序对窗口发送消息。

在第一次建立窗口时,Windows呼叫WndProc。在窗口关闭时,Windows也呼叫WndProc。窗口改变大小、移动或者变成图示时,从菜单中选择某一项目、挪动滚动条、按下鼠标按钮或者从键盘输入字符时,以及窗口显示区域必须被更新时,Windows都要呼叫WndProc。

所有这些WndProc呼叫都以消息的形式进行。在大多数Windows程序中,程序的主要部分都用来处理消息。Windows可以发送给窗口消息处理程序的消息通常都以WM开头的名字标识,并且都在WINUSER.H表头文件中定义。

实际上,从程序外呼叫程序内的例程这一种做法,在传统的程序设计中并非前所未闻。C中的signal函数可以拦截Ctrl-C中断或操作系统的其它中断。为MS-DOS编写的老程序中经常有拦截硬件中断的程序代码。?????

但在Windows中,这种概念扩展为包括一切事件。窗口中发生的一切都以消息的形式传给窗口消息处理程序。然后,窗口消息处理程序以某种方式响应这个消息,或者将消息传给DefWindowProc,进行内定处理。

在HELLOWIN中,窗口消息处理程序的wParam和lParam参数除了作为传递给DefWindowProc的参数外,不再有其它用处。这些参数给出了关于消息的其它信息,参数的含义与具体消息相关。

让我们来看一个例子。一旦窗口的显示区域大小发生了改变,Windows就呼叫窗口的窗口消息处理程序。窗口消息处理程序的hwnd参数是改变大小的窗口的句柄(请记住,一个窗口消息处理程序能处理依据同一个窗口类别建立的多个窗口的消息。参数hwnd让窗口消息处理程序知道是哪个窗口在接收消息)。参数message是WM_SIZE。消息WM_SIZE的参数wParam的值是SIZE_RESTORED、SIZE_MINIMIZED、SIZE_MAXIMIZED、SIZE_MAXSHOW或SIZE_MAXHIDE (在WINUSER.H表头文件中分别定义为数字0到4)。也就是说,参数wParam表明窗口是非最小化还是非最大化,是最小化、最大化,还是隐藏。

lParam参数包含了新窗口的大小,新宽度和新高度均为16位值,合在一起成为32位的lParam。WINDEF.H中提供了帮助程序写作者从lParam中取出这两个值的宏,我们将在说明这个宏。

有时候,DefWindowProc处理完消息后会产生其它的消息。例如,假设使用者执行HELLOWIN,并且使用者最终单击了 Close按钮,或者假设用键盘或鼠标从系统菜单中选择了 Close, DefWindowProc处理这一键盘或者鼠标输入,在检测到使用者选择了Close选项之后,它给窗口消息处理程序发送一条WM_SYSCOMMAND消息。WndProc将这个消息传给DefWindowProc。DefWindowProc给窗口消息处理程序发送一条WM_CLOSE消息来响应之。WndProc再次将它传给DefWindowProc。DefWindowProc呼叫DestroyWindow来响应这条WM_CLOSE消息。DestroyWindow导致Windows给窗口消息处理程序发送一条WM_DESTROY消息。WndProc再呼叫PostQuitMessage,将一条WM_QUIT消息放入消息队列中,以此来响应此消息。这个消息导致WinMain中的消息循环终止,然后程序结束。


队列化消息与非队列化消息

当我说消息是以一种有序的同步的方式进出时,我是说首先消息与硬件的中断不同。在一个窗口消息处理程序中处理消息时,程序不会被其它消息突然中断。

Although Windows programs can have multiple threads of execution,each thread's message queue handles messages for only the windows whose windows procedures are executed in that thread.

虽然Windows程序可以多线程执行,但每个线程的消息队列只处理那些窗口消息处理程序在本线程执行的消息。(?????)

虽然Windows程序可以多线程执行,但每个执行绪的消息队列只为窗口消息处理程序在该执行绪中执行的窗口处理消息。换句话说,消息循环和窗口消息处理程序不是并发执行的。当一个消息循环从其消息队列中接收一个消息,然后呼叫DispatchMessage将消息发送给窗口消息处理程序时,直到窗口消息处理程序将控制传回给Windows,DispatchMessage才能结束执行。


这也就是说窗口消息处理程序必须是可重入。在大多数情况下,这不会带来问题,但是程序写作者应该意识到这一点。例如,假设您在窗口消息处理程序中处理一个消息时设置了一个静态变量,然后呼叫了一个Windows函数。在这个函数传回时,您还能保证那个变数的值还是原来那个吗?难说--很可能您呼叫的Windows函数产生了另外一个消息,并且窗口消息处理程序在处理这个消息时改变了该变量的值。这也是在编译Windows程序时,有些编译最佳化选项必须关闭的原因之一。

在许多情况下,窗口消息处理程序必须保存它从消






阅读(911) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~