太子爷小说网 > 杂集电子书 > windows环境下32位汇编语言程序设计 >

第14节

windows环境下32位汇编语言程序设计-第14节

小说: windows环境下32位汇编语言程序设计 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!





图4。6  用CreateWindowEx建立的按钮



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
v上一页         回书目         下一页          
  


第4章 第一个窗口程序


4。2 分析窗口程序(4) 

    
4。2。3  消息循环

1。 消息循环的一般形式

程序中的以下代码就是通常的消息循环:

    。while  TRUE

        invoke  GetMessage;addr @stMsg;NULL;0;0

            。break  。if eax  0

            invoke  TranslateMessage;addr @stMsg

            invoke  DispatchMessage;addr @stMsg

    。endw

消息循环中的几个函数要用到一个MSG结构,用来做消息传递:

MSG STRUCT

  Hwnd      DWORD      ?

  Message   DWORD      ?

  WParam    DWORD      ?

  LParam    DWORD      ?

  Time      DWORD      ?

  Pt        POINT      

MSG ENDS

它的各个字段的含义是:

●   hwnd——消息要发向的窗口句柄。

●   message——消息标识符,在头文件中以WM_开头的预定义值(意思为Windows Message)。

●   wParam——消息的参数之一。

●   lParam——消息的参数之二。

●   time——消息放入消息队列的时间。

●   pt——这是一个POINT数据结构,表示消息放入消息队列时的鼠标坐标。

这个结构定义了消息的所有属性,GetMessage函数就是从消息队列中取出这样一条消息来的:

        invoke  GetMessage;lpMsg;hWnd;wMsgFilterMin;wMsgFilterMax

函数的lpMsg指向一个MSG结构,函数会在这里返回取到的消息,hWnd参数指定要获取哪个窗口的消息,例子中指定为NULL,表示获取的是所有本程序所属窗口的消息,wMsgFilterMin和wMsgFilterMax为0表示获取所有编号的消息。

GetMessage函数从消息队列里取得消息,填写好MSG结构并返回,如果获取的消息是WM_QUIT消息,那么eax中的返回值是0,否则eax返回非零值,所以用 。break 。if eax  0来检查返回值,如果消息队列中有WM_QUIT则退出消息循环。

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

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

2。 其他形式的消息循环

GetMessage函数是程序空闲的时候主动将控制权交还给Windows的一种方式,Windows是一个抢占式的多任务系统,任务之间每20 ms切换一次,试想一下,如果窗口程序在主窗口中采用死循环等待,消息由Windows直接发送到窗口过程,那么程序会是下列这种样子:

    invoke  CreateWindow,…

    invoke  ShowWindow,…

    invoke  UpdateWindow,…

    。while  dwQuitFlag  0    ;要退出时在窗口过程中设置dwQuitFlag

    。endw

    invoke  ExitProcess,…

但这样一来,即使程序在空闲状态,轮到自己的20 ms时间片的时候,CPU时间就会全部消耗在 。while循环中,使用GetMessage的时候,轮到应用程序时间片的时候,如果消息队列里还没有消息,那么程序还是停留在GetMessage内部,这时就可以由Windows当家做主没收这20 ms的时间片,如此保证了CPU资源的合理应用。

如果应用程序想把所有时间充分用回来,消息队列里没有消息的时候不让GetMessage在Windows内部等待,拱手交出属于自己的CPU时间,那么消息循环可以是下列这种样子:

    。while  TRUE

        invoke  PeekMessage;addr @stMsg;NULL;0;0;PM_REMOVE

        。if     eax

。break 。if @stMsg。message  WM_QUIT

invoke TranslateMessage;addr @stMsg

invoke DispatchMessage;addr @stMsg

        。else

                

        。endif

    。endw

PeekMessage是一个类似于GetMessage的函数,区别在于当消息队列里有消息的时候,PeekMessage取回消息,并在eax中返回非零值,没有消息的时候它会直接返回,并在eax中返回零。所以在返回非零值的时候,程序检查消息是否是WM_QUIT,是则结束消息循环,不是则用标准流程处理消息;返回零的时候,表示是空闲时间,程序就可以做其他工作了,但插入做其他工作的代码执行时间不能过长,以不超过10 ms为好,否则会影响正常的消息处理,使窗口的反应看起来很迟钝。如果必须处理很长时间的工作,那么应该将它分成很多小部分处理,以便有足够的频率来用PeekMessage来检查消息。PeekMessage的前面4个参数和GetMessage是相同的,增加了最后一个参数,PM_REMOVE表示取回消息的同时从消息队列里删除,否则用PM_NOREMOVE。

4。2。4  窗口过程

窗口过程是给Windows回调用的,它必须遵循规定的格式。对窗口过程的子程序名并没有规定,对Windows来说,窗口过程的地址才是惟一需要的,例子程序中的子程序名是_ProcWinMain,读者可以改用任何名称。窗口过程子程序的参数格式为:

WindowProc  proc    hwnd;uMsg;wParam;lParam

第一个参数是窗口句柄,一个窗口过程可能为多个基于同一个窗口类的窗口服务,所以Windows回调的时候必须指出要操作的窗口,否则窗口过程不知道要去处理哪个窗口,FirstWindow程序只建立了一个窗口,所以每次传递过来的hwnd和用CreateWindowEx函数返回的窗口句柄是一样的;第二个参数是消息标识,后面两个参数是消息的两个参数。这4个参数和消息循环中MSG结构中的前4个字段是一样的。

1。 窗口过程的结构

窗口过程一般有如下的结构:

WindowProc       proc       uses ebx edi esi;hWnd;uMsg;wParam;lParam

 

                 mov        eax;uMsg

                 。if        eax   WM_XXX

                            

                 。elseif    eax   WM_YYY

                            

                 。elseif    eax   WM_CLOSE

                            invoke  DestroyWindow;hWinMain

                            invoke  PostQuitMessage;NULL

                 。else

                            invoke  DefWindowProc;hWnd;uMsg;wParam;lParam

                            ret

                 。endif

                 xor        eax;eax

                 ret

 

WindowProc       endp



 
来源:电子工业出版社 作者:罗云彬 上一页         回书目         下一页          
上一页         回书目         下一页          
  


第4章 第一个窗口程序


4。2 分析窗口程序(5) 

    
该过程主要是对uMsg参数中的消息编号构成一个分支结构,对于需要处理的消息分别处理。不感兴趣的消息则交给DefWindowProc来处理。

要注意的是窗口过程中要注意保存ebx,edi,esi和ebp寄存器,高级程序中不用自己操心这一点,汇编中就要注意了,Windows内部将这4个寄存器当指针使用,如果返回时改变了它们的值,程序会马上崩溃。proc后面的uses伪操作在子程序进入和退出时自动安插上push和pop寄存器指令,来保护这些寄存器的值。其实不仅是在窗口过程中是这样,所有由应用程序提供给Windows的回调函数都必须遵循这个规定,如定时器回调函数等,所有Win32 API也遵循这个规定,所以调用API后,ebx,edi,esi和ebp寄存器的值总是不会被改变的,但ecx和edx的值就不一定了。

uMsg参数指定的消息有一定的范围,Windows标准窗口中已经预定义的值在0~03ffh之间,用户可以自定义一些消息,通过SendMessage等函数传给窗口过程做自定义的处理工作,这时可以使用的值是从0400h开始的,WM_USER就定义为00000400h,当程序员定义多个用户消息的时候,一般使用WM_USER+1,WM_USER+2,…之类的定义方法。

wParam和lParam参数是消息所附带的参数,它随消息的不同而不同,对于不同的消息,它们的含义必须分别从手册中查明:如WM_MOUSEMOVE消息中,wParam是标志,lParam是鼠标位置;而在WM_GETTEXT消息中,wParam是要获取的字符数,lParam是缓冲区地址;而对于WM_COPY消息来说,它不需要额外的信息,所以两个参数都没有定义。

处理了不同的消息,必须返回规定的值给Windows,返回值也需要分别从手册中查明,比如处理WM_CREATE消息的时候,如果返回0表示成功;如果程序无法初始化,如申请内存失败,那么可以返回-1,Windows就不会继续窗口的创建过程。一些消息的返回值则没有定义,但大部分的消息处理以后都以返回0表示成功,所以程序中把默认的返回语句放在最后,将eax清零后返回,如果在处理某个消息的时候需要返回不同的值,可以在分支中将eax赋值后直接用ret指令返回。对于DefWindowProc的返回值,我们不对它进行干涉,所以直接将eax不做修改地用ret返回。

WM_CLOSE消息是按下了窗口右上角的“关闭”按钮后收到的,程序可以在这里处理和关闭窗口相关的事情,一般是相关资源的释放工作,如释放内存、保存工作和提示用户是否保存工作等,如记事本程序在未保存的时候单击“关闭”按钮,会有提示框提示是否先保存文件,单击“取消”按钮的话,记事本不会关闭,这个步骤就是在WM_CLOSE消息处理中完成的。如果处理WM_CLOSE消息时直接返回,那么窗口不会关闭,因为这个消息只是Windows通知窗口用户单击了“关闭”按钮而已,窗口采取什么样的行为是窗口的事。当窗口决定关闭的时候,需要程序自己调用DestroyWindow来摧毁窗口,并用PostQuitMessage向消息循环发送WM_QUIT消息来退出消息循环。调用PostQuitMessage时的参数是退出码,就是GetMessage收到WM_QUIT后MSG结构wParam字段中的东西,在这里使用NULL。

 PostQuitMessage是初学者容易遗漏的函数,如果没有这条语句,外观上窗口是被摧毁掉,从屏幕上消失了,但主程序中的消息循环却没有收到WM_QUIT,结果还在那里打转。常有人调试的时候丢了这条语句,结果再一次编译的时候就收到错误:LINK fatal error LNK1104: cannot open file 〃xxx。exe〃,表示exe文件现在不可写。
    Windows为什么不在窗口摧毁的时候自动发送一个WM_QUIT消息,而必须由用户程序自己通过PostQuitMessage函数发送呢?其实很好理解:因为屏幕上可能不止一个窗口,Windows无法确定哪个窗口关闭代表着程序结束。试想一下,用户打开了一个输入参数的小窗口,单击“确定”按钮后关闭并回到主窗口,Windows却不分三七二十一自动发送了一个WM_QUIT,程序就会莫名其妙地退出了。

2。 收到消息的顺序

窗口过程收到消息是有一定顺序的,收到第一条消息并不是从消息循环开始以后,而是在CreateWindowEx中就开始了,显示和刷新窗口的函数ShowWindow和UpdateWindow也向窗口过程发送消息,这一点并不奇怪,因为Windows在CreateWindowEx前调用RegisterClassEx的时候就已经得到窗口过程的地址了。并且在建立窗口的过程中需要窗口过程的配合。表4。6和表4。7分别列出了调用CreateWindowEx和ShowWindow的时候窗口过程收到的消息。

表4。6  调用CreateWindowEx时窗口过程收到的消息

消息发生
 说    明

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的