?? 007.txt
字號(hào):
mov @dwY2,eax
invoke MoveToEx,_hDC,@dwX1,@dwY1,NULL
invoke LineTo,_hDC,@dwX2,@dwY2
ret
_DrawLine endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ShowTime proc _hWnd,_hDC
local @stTime:SYSTEMTIME
pushad
invoke GetLocalTime,addr @stTime
invoke _CalcClockParam
;********************************************************************
; 畫時(shí)鐘圓周上的點(diǎn)
;********************************************************************
invoke GetStockObject,BLACK_BRUSH
invoke SelectObject,_hDC,eax
invoke _DrawDot,_hDC,360/12,3 ;畫12個(gè)大圓點(diǎn)
invoke _DrawDot,_hDC,360/60,1 ;畫60個(gè)小圓點(diǎn)
;********************************************************************
; 畫時(shí)鐘指針
;********************************************************************
invoke CreatePen,PS_SOLID,1,0
invoke SelectObject,_hDC,eax
invoke DeleteObject,eax
movzx eax,@stTime.wSecond
mov ecx,360/60
mul ecx ;秒針度數(shù) = 秒 * 360/60
invoke _DrawLine,_hDC,eax,15
;********************************************************************
invoke CreatePen,PS_SOLID,2,0
invoke SelectObject,_hDC,eax
invoke DeleteObject,eax
movzx eax,@stTime.wMinute
mov ecx,360/60
mul ecx ;分針度數(shù) = 分 * 360/60
invoke _DrawLine,_hDC,eax,20
;********************************************************************
invoke CreatePen,PS_SOLID,3,0
invoke SelectObject,_hDC,eax
invoke DeleteObject,eax
movzx eax,@stTime.wHour
.if eax >= 12
sub eax,12
.endif
mov ecx,360/12
mul ecx
movzx ecx,@stTime.wMinute
shr ecx,1
add eax,ecx
invoke _DrawLine,_hDC,eax,30
;********************************************************************
popad
ret
_ShowTime endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi hWnd,uMsg,wParam,lParam
local @stPS:PAINTSTRUCT
mov eax,uMsg
.if eax == WM_TIMER
invoke InvalidateRect,hWnd,NULL,TRUE
.elseif eax == WM_PAINT
invoke BeginPaint,hWnd,addr @stPS
invoke _ShowTime,hWnd,eax
invoke EndPaint,hWnd,addr @stPS
.elseif eax == WM_CREATE
invoke SetTimer,hWnd,ID_TIMER,1000,NULL
;********************************************************************
.elseif eax == WM_CLOSE
invoke KillTimer,hWnd,ID_TIMER
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
;********************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
;********************************************************************
xor eax,eax
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
;********************************************************************
; 注冊窗口類
;********************************************************************
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
invoke LoadIcon,hInstance,ICO_MAIN
mov @stWndClass.hIcon,eax
mov @stWndClass.hIconSm,eax
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立并顯示窗口
;********************************************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
offset szClassName,offset szClassName,\
WS_OVERLAPPEDWINDOW,\
100,100,250,270,\
NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循環(huán)
;********************************************************************
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
下面簡單分析一下程序的結(jié)構(gòu)。
程序首先用標(biāo)準(zhǔn)的方法建立了一個(gè)窗口,在窗口的初始化消息WM_CREATE中用SetTimer建立了一個(gè)周期為1秒的定時(shí)器,用來在窗口的客戶區(qū)中繪畫時(shí)鐘。這個(gè)定時(shí)器在WM_CLOSE消息中用KillTimer函數(shù)撤銷。在定時(shí)器消息中,程序用InvalidateRect函數(shù)讓整個(gè)客戶區(qū)失效,相當(dāng)于讓W(xué)indows在消息循環(huán)中放入一條WM_PAINT消息,整個(gè)時(shí)鐘的繪畫在WM_PAINT消息中完成。
在WM_PAINT消息中程序用標(biāo)準(zhǔn)的方法調(diào)用BeginPaint函數(shù)獲取窗口客戶區(qū)的hDC,以便在上面繪畫時(shí)鐘,在消息返回的時(shí)候用EndPaint函數(shù)釋放hDC,兩個(gè)函數(shù)的中間,程序把hDC傳給_ShowTime子程序,由這個(gè)子程序完成整個(gè)繪畫工作。
在第6章中已經(jīng)講到:獲取系統(tǒng)時(shí)間不能依賴于WM_TIMER消息的計(jì)數(shù),所以在_ShowTime子程序的開始,程序調(diào)用GetLocalTime來獲取當(dāng)前的系統(tǒng)時(shí)間,并根據(jù)這個(gè)時(shí)間來繪畫時(shí)鐘的時(shí)、分、秒指針。由于繪畫的過程很快,所以整個(gè)程序的結(jié)構(gòu)使用前面圖7.1中所示的A結(jié)構(gòu),也就是每次有WM_PAINT消息的時(shí)候,程序總是重畫整個(gè)客戶區(qū),所以讀者在速度比較慢的計(jì)算機(jī)上運(yùn)行這個(gè)程序時(shí),可能會(huì)看到有個(gè)閃爍的過程,因?yàn)槌绦蛎看慰偸窍葘⒄麄€(gè)客戶區(qū)清除成背景色(InvalidateRect函數(shù)最后的TRUE參數(shù)要求Windows在發(fā)送WM_PAINT消息前清除客戶區(qū)),然后繪畫四周的刻度,最后畫上指針。繪畫刻度是由_DrawDot子程序完成的,繪畫指針是由_DrawLine子程序完成的。
GetLocalTime后面的_CalcClockParam子程序根據(jù)客戶區(qū)的尺寸計(jì)算時(shí)鐘尺寸參數(shù),它比較客戶區(qū)高度和寬度,以其中的較小值用做時(shí)鐘的直徑,計(jì)算得到的圓心最后存放于全局變量dwCenterX和dwCenterY中,計(jì)算得到的半徑存放于dwRadius中。
程序中有兩個(gè)公用的子程序:_CalcX和_CalcY,它們用來計(jì)算角度對應(yīng)的坐標(biāo),如圖7.5所示,時(shí)鐘0點(diǎn)時(shí)間是從垂直方向開始的,以時(shí)間值為角度配合Windows的默認(rèn)坐標(biāo)系,對應(yīng)某個(gè)時(shí)間點(diǎn)(x,y),x應(yīng)該是圓心x加上角度的正弦值乘以半徑,y應(yīng)該是圓心y減去角度的余弦值乘以半徑。_CalcX和_CalcY輸入的參數(shù)是角度_dwDegree和半徑_dwRadius。子程序中使用80x86的協(xié)處理器指令,首先將角度值換算成弧度值——乘以π并除以180,然后用上面分析的公式進(jìn)行浮點(diǎn)計(jì)算并將結(jié)果返回。
圖7.5 時(shí)鐘程序的坐標(biāo)計(jì)算
在接下來的內(nèi)容中,先介紹一些繪畫操作的背景知識(shí)。
7.2.1 畫筆和畫刷
GDI中的繪畫函數(shù)有3大類:畫點(diǎn)、畫線和畫填充區(qū)域。使用過Photoshop等圖形軟件的讀者一定知道,在畫線之前需要選擇一種畫筆,這樣畫出來的線條都是基于這種畫筆的;同樣,填充一個(gè)區(qū)域之前需要選擇一種畫刷,這樣整個(gè)填充區(qū)域?qū)⒅貜?fù)使用這個(gè)畫刷的顏色或圖案。
GDI中也有同樣的畫筆和畫刷的概念,畫筆、畫刷以及其他一些GDI中要使用的東西,包括字體、區(qū)域、路徑、圖案和位圖統(tǒng)稱GDI中的“對象”,通過SelectObject函數(shù)可以指定一個(gè)DC當(dāng)前使用的對象對應(yīng)哪個(gè)對象句柄,稱為“當(dāng)前對象”,當(dāng)設(shè)置了一個(gè)當(dāng)前對象的時(shí)候,以后和這種對象相關(guān)的函數(shù)都將使用當(dāng)前對象,直到再次用SelectObject選擇新的對象為止。比如選擇了新的畫筆后,以后所有畫線函數(shù)畫出來的線條樣式都是由這個(gè)畫筆決定的,而選擇了新的畫刷后,則所有填充函數(shù)填充的樣式都將使用這個(gè)畫刷。
SelectObject函數(shù)的用法是:
invoke SelectObject,hDC,hGDIObject
mov hOldObject,eax
其中參數(shù)hGDIObject就是對象的句柄,它可以是位圖句柄、畫筆句柄、畫刷句柄、字體句柄或區(qū)域句柄,函數(shù)會(huì)根據(jù)句柄的種類自動(dòng)替換原有的對象,并將原來使用的對象句柄返回(當(dāng)對象類型是區(qū)域的時(shí)候除外),如果DC中原來沒有設(shè)置當(dāng)前對象,那么函數(shù)的返回值是GDI_ERROR或NULL。
1. 使用預(yù)定義的畫筆和畫刷
Windows預(yù)定義了一些常用的畫筆和畫刷,在程序中可以用GetStockObject來獲取它們的句柄,Stock的中文含義是“常備的、庫存的”,所以這個(gè)函數(shù)字面上的意思就是“獲取常用的對象”,注意并沒有類似于GetStockPen或GetStockBrush之類的函數(shù),所有獲取常用對象的操作統(tǒng)一使用GetStockObject函數(shù)。
GetStockObject函數(shù)的用法是:
invoke GetStockObject,fnObject
mov hObject,eax
fnObject參數(shù)是預(yù)定義的對象類型,可以是表7.2所示的取值。
表7.2 GDI中的常用對象
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -