友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
windows环境下32位汇编语言程序设计-第31部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
2。 GDI程序的结构
对于Win32程序来说,WM_PAINT消息随时可能发生,这就意味着,程序再也不能像在DOS下一样输出结果后就不管了,反过来,程序在任何时刻都应该知道如何恢复整个或局部客户区中以前输出的内容,本着这个要求,可以按图7。1所示来安排程序结构。
图7。1 GDI程序的结构
如果程序的功能比较简单,可以采取图中左边的A程序结构,即计算及刷新整个客户区的代码全部安排在WM_PAINT消息中完成,这样,每次当客户区的全部或部分需要被更新的时候,程序重新执行整个生成客户区屏幕数据的功能模块并刷新客户区。这种结构适用于功能模块很短小且执行速度很快的情况,整个过程的时间最好不超过几百ms,否则,用户会在一个明显的等待时间后才看到程序把客户区中的“空洞”补上。考虑一个极端的情况:当程序输出的内容是经过千辛万苦才算出来的——这不是一件奇怪的事情,计算圆周率的程序就要动辄计算几个小时——那么即使客户区被别的窗口覆盖掉一点点,程序也要经过整个计算过程后才能重画客户区,而且在这个过程中,程序还没有从WM_PAINT消息返回,以至于无法处理其他消息,结果程序就会以客户区中有个空洞的难看姿势呆在屏幕上一动不动达几个小时!
当生成屏幕数据的功能模块有些复杂的时候,如刚才计算圆周率的例子,就应该考虑采用图中B程序所示的结构了。在这个程序中,功能模块和客户区刷新模块分别在不同的子程序中实现,功能模块单独用一个子程序完成,这个子程序可以由用户通过选择菜单项在WM_MAND消息中执行,也可以新建另外一个线程来完成,总之,它最后把计算结果放到一个缓冲区中,而每当客户区需要刷新时,程序在WM_PAINT消息中调用客户区刷新子程序,这个子程序从计算好的缓冲区中取出数据并输出到客户区中,由于单纯的屏幕刷新过程是很快的,所以用户根本来不及看到客户区中的空洞。
在本章后面的内容中有两个时钟的例子:Clock。exe和BmpClock。exe,前面一个例子采用的是A结构,后面一个例子采用的是B结构,读者在阅读的时候可以比较一下它们在结构上的不同。
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
7。1 GDI原理(2)
3。 探讨WM_PAINT消息
当客户区被覆盖并重新显示的时候,Windows并不是在所有的情况下都发送WM_PAINT消息,下面是几种不同的情况:
● 当鼠标光标移过窗口客户区以及图标拖过客户区这两种情况,Windows总是自己保存被覆盖的区域并恢复它,并不需要发送WM_PAINT消息通知用户程序。
● 当窗口客户区被自己的下拉式菜单覆盖,或者被自己弹出的对话框覆盖后,Windows会尝试保存被覆盖的区域并在以后恢复它,如果因为某种原因无法保存并恢复的话,Windows会发送一个WM_PAINT消息通知程序。
● 别的情况造成窗口的一部分从不可见变到可见,如程序从最小化的状态恢复,其他的窗口覆盖客户区后移开,用户改变了窗口的大小和用户按动滚动条等,在这些情况下,Windows会向窗口发送WM_PAINT消息。
● 一些函数会引发WM_PAINT消息,如UpdateWindow,InvalidateRect以及InvalidateRgn函数等。
窗口过程收到WM_PAINT消息后,并不代表整个客户区都需要被刷新,有可能客户区被覆盖的区域只有一小块,这个区域就叫做“无效区域”,程序只需要更新这个区域。
和WM_TIMER消息类似,WM_PAINT消息也是一个低级别的消息,虽然它不会像WM_TIMER消息一样被丢弃,但Windows总是在消息循环空的时候才把WM_PAINT放入其中,实际上,Windows为每个窗口维护一个“绘图信息结构”,无效区域的坐标就在其中,每当消息循环空的时候,如果Windows发现存在一个无效区域,就会放入一个WM_PAINT消息。
无效区域的坐标并不附带在WM_PAINT消息的参数中,在程序中有其他方法可以获取,WM_PAINT消息只是通知程序有个区域需要更新而已,所以Windows也不会同时将两条WM_PAINT消息放入消息循环,当Windows要放入一条WM_PAINT消息的时候,如果发现已经存在一个无效区域了,那么它只需要把新旧两个无效区域合并计算出一个新的无效区域就可以了,消息循环中还是只需要一条WM_PAINT消息。
由于存在“无效区域”这样一个东西,所以程序在WM_PAINT消息中对客户区刷新完毕后工作并没有结束,如果不使无效区域变得有效,Windows会在下一轮消息循环中继续放入一个WM_PAINT消息。还记得4。4。2节中的实验4吗,如果没有这个环节,WM_PAINT消息就会源源不断地发过来!那个实验中我们并没有去刷新客户区,而是简单地用一个ValidateRect函数直接让客户区变得有效,以此来“欺骗”Windows已经没有无效区域了,当Windows检查“绘图信息结构”的时候发现没有了无效区域,也就不会继续发送WM_PAINT消息了。
WM_PAINT消息的处理流程一般是:
。if eax WM_PAINT ;eax为uMsg
invoke BeginPaint;hWnd;addr stPS
;刷新客户区的代码
invoke EndPaint;hWnd;addr stPS
xor eax;eax
ret
读者可以发现中间并没有调用ValidateRect来使无效区域变得有效,这是因为BeginPaint函数和EndPaint函数隐含有这个功能,如果不是以BeginPaint/EndPaint当做消息处理代码的头尾的话,那么在WM_PAINT消息返回的时候就必须调用ValidateRect函数。
BeginPaint函数的第二个参数是一个绘图信息结构的缓冲区地址,Windows会在这里返回绘图信息结构,结构中包含了无效区域的位置和大小,绘图信息结构的定义如下:
PAINTSTRUCT STRUCT
hdc DWORD ?
fErase DWORD ?
rcPaint RECT
fRestore DWORD ?
fIncUpdate DWORD ?
rgbReserved BYTE 32 dup(?)
PAINTSTRUCT ENDS
其中hdc字段是窗口的设备环境句柄(在下一节中将要讲到),rcPaint字段是一个RECT结构,它指定了无效区域矩形的对角顶点,fErase字段如果为非零值,表示Windows在发送WM_PAINT消息前已经用背景色擦除了无效区域,后面3个字段是Windows内部使用的,应用程序不必去理会它们。
7。1。2 设备环境
好了,解决了“When”的问题,让我们来考虑一个新的问题,在DOS操作系统中,向屏幕输出数据实际上是把输出内容拷贝到视频缓冲区中,在第1章的图1。1中就已经说明:如果在文本模式下显示信息,只需要把内容拷贝到B8000h处的内存中;显示图形信息,可以把图形数据拷贝到A0000h处的内存中。
在Windows中,GDI接口把程序和硬件分隔开来,在Win32编程中,再也不能通过直接向视频缓冲区拷贝数据的办法来显示信息了,那么,究竟该往哪里输出图形呢——这就是“Where”的问题。答案是:通过“设备环境”来输出图形。
1。 什么是设备环境
在Windows中,所有与图形相关的操作都是用统一的方法来完成的(不然就不能称为“图形设备接口”了)。不管是绘画屏幕上的一个窗口,还是把图形输出到打印机,或者对一幅位图进行绘画,使用的绘图函数都是相同的,为了实现方法上的统一,必须将所有的图形对象看成是一个虚拟的设备,这些设备可能有不同的属性,如黑白打印机和彩色屏幕的颜色深度是不同的,不同打印机的尺寸和分辨率可能是不同的,绘图仪只支持矢量而不支持位图等。不同设备的不同属性就构成了一个绘图的“环境”,就像DOS操作系统中把视频缓冲区当做图形操作的对象一样,这个绘图的“环境”就是Win32编程中图形操作的对象,把它叫做“设备环境”。设备环境实际上是一个数据结构,结构中保存的就是设备的属性,当对设备环境进行图形操作的时候,Windows可以根据这些属性找到对应的设备进行相关的操作。
在实际使用中,通过“设备环境”可以操作的对象很广泛,除了可以是打印机或绘图仪等硬件设备外,也可以是窗口的客户区,包括大大小小的所有可以被称为窗口的按钮与控件等的客户区,也可以是一个位图。总之,任何需要用到图形操作的东西都可以通过“设备环境”进行绘图。
为了更好地理解“设备环境”是什么,先来看一个例子,例子的代码在所附光盘的Chapter07DcCopy目录中,DcCopy。asm中的代码如下:
。386
。model flat;stdcall
option casemap:none
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; Include 文件定义
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
include windows。inc
include gdi32。inc
includelib gdi32。lib
include user32。inc
includelib user32。lib
include kernel32。inc
includelib kernel32。lib
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
ID_TIMER equ 1
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; 数据段
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
。data?
hInstance dd ?
hWin1 dd ?
hWin2 dd ?
nst
szClass1 db 'SourceWindow';0
szClass2 db 'DestWindow';0
szCaption1 db '请尝试用别的窗口覆盖本窗口!';0
szCaption2 db '本窗口图像拷贝自另一窗口';0
szText db 'Win32 Assembly; Simple and powerful !';0
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
de
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; 定时器过程
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
_ProcTimer proc _hWnd;uMsg;_idEvent;_dwTime
local @hDc1;@hDc2
local @stRect:RECT
invoke GetDC;hWin1
mov @hDc1;eax
invoke GetDC;hWin2
mov @hDc2;eax
invoke GetClientRect;hWin1;addr @stRect
invoke BitBlt;@hDc2;0;0;@stRect。right;@stRect。bottom;
来源:电子工业出版社 作者:罗云彬 上一页 回书目 下一页
上一页 回书目 下一页
第7章 图形操作
7。1 GDI原理(3)
@hDc1;0;0;SRCCOPY
invoke ReleaseDC;hWin1;@hDc1
invoke ReleaseDC;hWin2;@hDc2
ret
_ProcTimer endp
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
; 窗口过程
;》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
_ProcWinMain proc uses ebx edi esi;hWnd;uMsg;wParam;lParam
local @stPs:PAINTSTRUCT
local @stRect:RECT
local @hDc
mov eax;uMsg
mov ecx;hWnd
;********************************************************************
。if eax WM_PAINT && ecx hWin1
invoke BeginPaint;hWnd;addr @stPs
mov @hDc;eax
invoke GetClientRect;hWnd;addr @stRect
invoke DrawText;@hDc;addr szText;…1;
addr @stRect;
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint;hWnd;addr @stPs
;********************************************************************
。elseif eax WM_CLOSE
invoke PostQuitMessage;NULL
invoke DestroyWindow;hWin1
invoke DestroyWindow;hWin2
;********************************************************************
。else
invoke D
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!