Code

 

 

我们看上面的流程,可以总结出创建一个窗口的流程:

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

2)建立窗口(CreateWindow)。

3)显示窗口(ShowWindows)。

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

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

二.那么为什么要按照这样的顺序做呢?我们来用下图阐述一下原理:

(1).当我们按了键盘和shu标后,此时会产生一个消息(包含消息的类型,发生的时间,位置等),并放入到系统消息队列中。此时windows会检查消息发生的位置,如果发现这个消息刚好位于某个应用程序的窗口内的时候,就将这个消息放于应用程序的消息队列中。如图c所示。

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

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

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

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

三.理解了原理之后,我们来仔细分析上面创建一个窗口的程序。

(1).为什么要使用注册窗口类?

例如:在一个窗口中,可能有不同的按钮,它们的工作原理都是一样的。但是各个按钮可能都有不同的表现形式,比如大小,颜色等。所以在这种情况下,我们需要将创建窗口的共性提取出来,然后再设置每个具体的窗口。这样就更加符合面向对象的原理。

(2).当应用程序取得消息后,DispatchMessage是如何知道要发给谁去处理呢?

这是因为在注册窗口类时,已经指定了。否则windows不可能知道。

(3).注册窗口类后,就要创建窗口了,建立窗口以后,传回来的是窗口句柄,要把它先保存起来,这时候,窗口虽已建立,但还没有在屏幕上显示出来,要用ShowWindow把它显示出来,ShowWindow也可以用在别的地方,主要用来控制窗口的显示状态(显示或隐藏),大小控制(最大化、最小化或原始大小)和是否激活(当前窗口还是背后的窗口),它用窗口句柄做第一个参数,第二个参数则是显示的方式

 

4.窗口建立后,我们需要在窗口的客户区显示内容,这就要用到UpdateWindow, 它实际上就是向窗口发送了一条WM_PAINT消息

 

三.消息循环

函数会在这里返回取到的消息,hWnd参数指定要获取哪个窗口的消息,例子中指定为NULL,表示获取的是所有本程序所属窗口的消息,wMsgFilterMinwMsgFilterMax0表示获取所有编号的消息。

GetMessage函数从消息队列里取得消息,填写好MSG结构并返回

TranslateMessageMSG结构传给Windows进行一些键盘消息的转换,当有键盘按下和放开时,Windows产生WM_KEYDOWNWM_KEYUPWM_SYSKEYDOWNWM_SYSKEYUP消息,但这些消息的参数中包含的是按键的扫描码,转换成常用的ASCII码要经过查表,很不方便,TranslateMessage遇到键盘消息则将扫描码转换成ASCII码并在消息队列中插入WM_CHARWM_SYSCHAR消息,参数就是转换好的ASCII码,如此一来,要处理键盘消息的话只要处理WM_CHAR消息就好了。遇到别的消息则TranslateMessage不做处理。

例如:如果我们按一个健盘上的健,如果不用TranslateMessage,则会处理WM_KEYDOWNWM_KEYUP消息,否则,就只会产生WM_CHAR消息。

最后,由DispatchMessage将消息发送到窗口对应的窗口过程去处理。窗口过程返回后DispatchMessage函数才返回,然后开始新一轮消息循环。

 

四.       WM_PAINT消息

在最初创建窗口时,整个客户区域是无效的。因为程序还没有在窗口上画什么东西。当调 updatawindow后,会发送一个WM_PAINT消息(第一个WM_PAINT消息),它会指示在窗口的客户区内画一些东西。

那么当我们改变窗口大小时,使整个窗口无效,迫使windows刷新。

当窗口与另一个窗口重叠时,当移开后,使被重叠的那部分无效。

当最小化窗口后,当窗口重新恢复到原始状态时,windows并不保存客户区的内容(因为那样工作量太大),只是使之无效而已。

 一旦客户区域失效,窗口过程就会接收一个新的WM_PAINT消息,此时使用GetClientRect获得变化后的客户区哉。并在新窗口的中央显示文本。

 

 

 

 

 

 

 

 

posted on 2009-04-24 13:05  jasonM  阅读(2127)  评论(0编辑  收藏  举报