不能免俗,还是从Hello world来谈起

Code

 

 

窗口程序的运行过程

在屏幕上显示一个窗口的过程一般有以下步骤,这就是主程序的结构流程:

1)得到应用程序的句柄(GetModuleHandle)。

2)注册窗口类(RegisterClassEx)。在注册之前,要先填写RegisterClassEx的参数WNDCLASSEX结构。

3)建立窗口(CreateWindowEx)。

4)显示窗口(ShowWindows)。

5)刷新窗口客户区(UpdateWindow)。

6)进入无限的消息获取和处理的循环。首先获取消息(GetMessage),如果有消息到达,则将消息分派到回调函数处理(DispatchMessage),如果消息是WM_QUIT,则退出循环。

程序的另一半_ProcWinMain子程序是用来处理消息的,它就是窗口的回调函数(Callback),也叫做窗口过程,之所以是回调函数是因为它是由Windows而不是我们自己调用的,我们调用DispatchMessage,而DispatchMessage再回过来调用窗口过程。

所有的用户操作都是通过消息来传给应用程序的,如用户按键,鼠标移动,选择了菜单和拖动了窗口等,应用程序中由窗口过程接收消息并处理,在例子程序中就是_ProcWinMain。窗口过程构造了一个分支结构,对应不同的消息执行不同的代码,所以一个应用程序中几乎所有的功能代码都集中在窗口过程里。

窗口程序运行中消息传输的流程可以由图4.4来表示。

先来看看Windows对消息的处理。Windows在系统内部有一个系统消息队列,当输入设备有所动作的时候,如用户按动了键盘、移动了鼠标,按下或放开了鼠标等,Windows都会产生相应的记录放在系统消息队列里,如图4.4中的箭头ab所示,每个记录中包含消息的类型、发生的位置(如鼠标在什么坐标移动)和发生的时间等信息。

 

4.4  窗口程序的运行过程

同时,Windows为每个程序(严格地说是每个线程)维护一个消息队列,Windows检查系统消息队列里消息的发生位置,当位置位于某个应用程序的窗口范围内的时候,就把这个消息派送到应用程序的消息队列里,如图4.4中的箭头c所示。

当应用程序还没有来取消息的时候,消息就暂时保留在消息队列里,当程序中的消息循环执行到GetMessage的时候,控制权转移到GetMessage所在的USER32.DLL中(箭头1),USER32.DLL从程序消息队列中取出一条消息(箭头2),然后把这条消息返回应用程序(箭头3)。

应用程序可以对这条消息进行预处理,如可以用TranslateMessage把基于键盘扫描码的按键消息转换成基于ASCII码的键盘消息,以后也会用到TranslateAccelerator把键盘快捷键转换成命令消息,但这个步骤不是必需的。

然后应用程序将处理这条消息,但方法不是自己直接调用窗口过程来完成,而是通过DispatchMessage间接调用窗口过程,Dispatch的英文含义是“分派”,之所以是“分派”,是因为一个程序可能建有不止一个窗口,不同的窗口消息必须分派给相应的窗口过程。当控制权转移到USER32.DLL中的DispatchMessage时,DispatchMessage找出消息对应窗口的窗口过程,然后把消息的具体信息当做参数来调用它(箭头5),窗口过程根据消息找到对应的分支去处理,然后返回(箭头6),这时控制权回到DispatchMessage,最后DispatchMessage函数返回应用程序(箭头7)。这样,一个循环就结束了,程序又开始新一轮的GetMessage

几点疑问解释:

1.为什么要由windows调用窗口过程,由我们程序取了消息后,自己调用窗口过程处理不更方便吗?

(1).消息可分为“进队消息”和“不进队消息”。进队消息是由Windows放入程序消息队列,在程序的消息循环中,重新返回并分配给窗口;不进队消息是Windows直接调用窗口过程。
   一般进队消息都是用户输入的结果。以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)、鼠标击键(WM_LBUTTONDOWN)的形式给出。进队消息还包括时钟消息(WM_TIMER)、刷新消息(WM_PAINT)、退出消息(WM_QUIT)。
   不进队消息许多情况都来自调用特定的Windows函数。调用CreateWindow后发送一个WM_CREATE消息;调用ShowWindow后发送WM_SIZE和WM_SHOWWINDOW消息;调用UpdateWindow发送WM_PAINT消息。

也就是说,对不进队的消息,我们无法做到手工调用窗口过程,它本身就是直接调用窗口过程。

(2).一个窗口过程能处理基于同一个窗口类创建的多个窗口的消息,参数hwnd让窗口过程知道哪个窗口在接受消息.如果程序自己处理消息的“分派”,就必须自己维护本程序所属窗口的列表,当程序建立的窗口不止一个的时候,这个工作就变得复杂起来,你不可能去做到每当创建一个新窗口时,就去手动调用窗口过程。那样,你要累死的。

(3).别的程序也可能用SendMessage通过Windows直接调用你的窗口过程.
2.PostMessageSendMessage

应用程序之间也可以互发消息,PostMessage是把一个消息放到其他程序的消息队列中,如图4.4中箭头d所示,目标程序收到了这条消息就把它放入该程序的消息队列去处理;而SendMessage则越过消息队列直接调用目标程序的窗口过程(如图4.4中箭头I所示),窗口过程返回以后才从SendMessage返回(如图4.4中箭头II所示)。

窗口过程是由Windows回调的,Windows又是怎么知道往哪里回调呢?答案是我们在调用RegisterClassEx函数的时候告诉了Windows


 

 

 

 

posted on 2009-04-12 14:19  jasonM  阅读(382)  评论(0编辑  收藏  举报