📄 见招拆招《windows程序设计》(七) .txt
字号:
mov stPrevious.wSecond,ax;
xor eax,eax
ret
.elseif message == WM_PAINT
invoke BeginPaint, hwnd, ADDR ps
mov hdc,eax ; Get handle to device context
invoke SetIsotropic, hdc,cxClient,cyClient
invoke DrawClock, hdc; Draw the dial
invoke EndPaint, hwnd, ADDR ps
xor eax,eax
ret
.elseif message == WM_DESTROY
invoke KillTimer,hwnd,ID_TIMER
invoke PostQuitMessage,NULL
ret
.endif
invoke DefWindowProc,hwnd, message, wParam, lParam
ret
WndProc endp
;---------------------------------------------------------------;
; This routine finds new values for cartesian coordinates X & Y ;
; when the point is rotated by theta degrees ;
;;
; Enter: DS:SI points at a data block in the calling routine ;
;;
; [si] (real4) contains the rotation angle in degrees ;
; [si+4] (real4) contains the math X coordinate;
; [si+8] (real4) contains the math Y coordinate;
; [si+12](real4) will contain the new X coordinate ;
; [si+16](real4) will contain the new Y coordinate ;
;;
; Note: I keep the orignal coords (as opposed to updating ;
; so that a program can reuse the same values if required ;
;; ;
; X1 = X * cos(theta) + Y * sin(theta) ;
; Y1 = -X * sin(theta) + Y * cos(theta) ;
; ;
; Return: New coords are updated in callers parameter block. ;
;---------------------------------------------------------------;
.DATA
deg2rad real4 1.7453292E-2 ; 2 * pi / 360 (conversion factor for radians)
.CODE
angle equ dword ptr [esi]
X_Coord equ dword ptr [esi+4]
Y_Coord equ dword ptr [esi+8]
New_X equ dword ptr [esi+12]
New_Y equ dword ptr [esi+16]
rotate PROC
fld angle ; Put the rotation in degrees into st(0)
fmul deg2rad ; st=radians
fsincos; st=cos, st(1)=sin
fld st ; st=cos, st(1)=cos, st(2)=sin
fmul X_Coord ; st=X*cos, st(1)=cos, st(2)=sin
fxch; st=cos, st(1)=X*cos, st(2)=sin
fmul Y_Coord ; st=Y*cos, st(1)=X*cos, st(2)=sin
fxch st(2) ; st=sin, st(1)=X*cos, st(2)=Y*cos
fld st ; st=sin, st1=sin, st2=X*cos, st3=Y*cos ;now X, X, Y*sin, Y*cos, cos, sin
fmul X_Coord ; X*sin, sin, X*cos, Y*cos
fxch; sin, X*sin, X*cos, Y*cos
fmul Y_Coord ; Y*sin, X*sin, X*cos, Y*cos
fadd st,st(2); Y*sin + X*cos, X*sin, X*cos, Y*cos
fstp New_X ; X*sin, X*cos, Y*cos ; Stored new X coord
fxch st(1) ; X*cos, X*sin, Y*cos
fstp st ; X*sin, Y*cos
fsub; -X*sin +Y*cos
fstp New_Y ; Stored new Y coord
RET
rotate ENDP
END START
CLOCK屏幕显示如图8-2。
图8-2 CLOCK的屏幕显示
等方向性(isotropic)映像对于这样的应用来说是理想的,CLOCK.C中的SetIsotropic函数负责设定此模式。在呼叫SetMapMode之后,SetIsotropic将窗口范围设定为1000,并将视端口范围设定为显示区域的一半宽度和显示区域的负的一半高度。视端口原点被设定为显示区域的中心。我在第五章中讨论过,这将建立一个笛卡儿坐标系,其点(0,0)位于显示区域的中心,在所有方向上的范围都是1000。
RotatePoint函数是用到三角函数的地方,此函式的三个参数分别是一个或者多个点的数组、数组中点的个数以及以度为单位的旋转角度。函式以原点为中心按顺时针方向(这对一个时钟正合适)旋转这些点。例如,如果传给函式的点是(0,100)-即12:00的位置-而角度为90度,那么该点将被变换为(100,0)-即3:00。它使用下列公式来做到这一点:
x' = x * cos (a) + y * sin (a)
y' = y * cos (a) - x * sin (a)
RotatePoint函数在绘制时钟表面的点和表针时都是有用的,我们将马上看到这一点。
DrawClock函数绘制60个时钟表面的点,从顶部(12:00)开始,其中每个点离原点900单位,因此第一个点位于(0,900),此后的每个点按顺时针依次增加6度。这些点中的l2个直径为100个单位;其余的为33个单位。使用Ellipse函数来画点。
DrawHands函数绘制时钟的时针、分针和秒针。定义表针轮廓(当它们垂直向上时的形状)的坐标存放在一个POINT结构的数组中。根据时间,这些坐标使用RotatePoint函数进行旋转,并用Windows的Polyline函数进行显示。注意时针和分针只有当传递给DrawHands的bChange参数为TRUE时才被显示。当程序更新时钟的表针时,大多数情况下时针和分针不需要重画。
现在让我们将注意力转到窗口消息处理程序。在WM_CREATE消息处理期间,窗口消息处理程序取得目前时间并将它存放在名为dtPrevious的变量中,这个变量将在以后被用于确定时针或者分针从上次更新以来是否改变过。
第一次绘制时钟是在第一个WM_PAINT消息处理期间,这只不过是依次呼叫SetIsotropic、DrawClock和DrawHands,后者的bChange参数被设定为TRUE。
在WM_TIMER消息处理期间,WndProc首先取得新的时间并确定是否需要重新绘制时针和分针。如果需要,则使用一个白色画笔和上一次时间绘制所有的表针,从而有效地擦除它们。否则,只对秒针使用白色画笔进行擦除,然后,再使用一个黑色画笔绘制所有的表针。
以定时器进行状态报告
本章的最后一个程序是我在第五章提到过的。它是一个使用GetPixel函数的好例子。
WHATCLR (见程序8-5)显示了鼠标光标下目前图素的RGB颜色。
程序8-5 WHATCLR
WHATCLR.Asm 这个程序的源程序有一点问题,不知道为什么它要在每次取点之后画上一个黑色的点,这样会导致刚开始取色是
正常的,但是时间长一些,取出来的就是黑色了。下面的程序是我修改过的。
;MASMPlus 代码模板 - 普通的 Windows 程序代码
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
FindWindowSize PROTO :DWORD,:DWORD
ID_TIMER equ 1
.DATA
szAppName db "WhatClr",0
fFlipFlop BOOL FALSE
.DATA?
hInstance dd ?
cr COLORREF ?
crLast COLORREF ?
hdcScreen HDC ?
.CODE
START: ;从这里开始执行
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0
WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
LOCAL hWnd :HWND
LOCAL cxWindow,cyWindow:DWORD
mov wndclass.cbSize,sizeof WNDCLASSEX
mov wndclass.style,CS_HREDRAW or CS_VREDRAW
mov wndclass.lpfnWndProc,offset WndProc
mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0
push hInst
pop wndclass.hInstance
invoke LoadIcon,NULL,IDI_APPLICATION
mov wndclass.hIcon,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax
invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,EAX
mov wndclass.lpszMenuName,NULL
mov wndclass.lpszClassName,offset szAppName
mov wndclass.hIconSm,0
invoke RegisterClassEx, ADDR wndclass
.if (EAX==0)
invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),addr szAppName,MB_ICONERROR
ret
.endif
lea eax,cyWindow
push eax
lea eax,cxWindow
push eax
call FindWindowSize
invoke CreateWindowEx,
NULL,
ADDR szAppName, ;window class name
CTXT("What Color"), ;window caption
WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_BORDER, ;window style
CW_USEDEFAULT,;initial x position
CW_USEDEFAULT,;initial y position
cxWindow, ;initial x size
cyWindow, ;initial y size
NULL,;parent window handle
NULL,;window menu handle
hInstance,;program instance handle
NULL;creation parameters
mov hWnd,eax
invoke ShowWindow,hWnd,iCmdShow
invoke UpdateWindow,hWnd
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
mov eax,msg.wParam
ret
WinMain endp
FindWindowSize proc pcxWindow:DWORD,pcyWindow:DWORD
LOCAL hdcFindWin:HDC
LOCAL tm:TEXTMETRIC
invoke CreateIC,CTEXT("DISPLAY"),NULL,NULL,NULL
mov hdcFindWin,eax
invoke GetTextMetrics,hdcFindWin,addr tm
invoke DeleteDC,hdcFindWin
;* pcxWindow = 2 * GetSystemMetrics (SM_CXBORDER)+12 * tm.tmAveCharWidth ;
invoke GetSystemMetrics,SM_CXBORDER
shl eax,1
mov ebx,eax
mov eax,tm.tmAveCharWidth
mov ecx,12
mul ecx
add eax,ebx
mov edi,pcxWindow
mov [edi],eax
; * pcyWindow = 2 * GetSystemMetrics (SM_CYBORDER)+GetSystemMetrics (SM_CYCAPTION) +
; 2 * tm.tmHeight ;
invoke GetSystemMetrics,SM_CYBORDER
shl eax,1
mov ebx,eax
invoke GetSystemMetrics,SM_CYCAPTION
add ebx,eax
mov eax,tm.tmHeight
shl eax,1
add ebx,eax
mov edi,pcyWindow
mov [edi],ebx
ret
FindWindowSize Endp
WndProc proc hwnd:DWORD,message:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc:HDC
LOCAL ps :PAINTSTRUCT
LOCAL pt :POINT
LOCAL rc :RECT
LOCAL szBuffer[16]:TCHAR
.if message == WM_CREATE
invoke CreateDC,CTEXT("DISPLAY"),NULL,NULL,NULL
mov hdcScreen,eax
invoke SetTimer,hwnd,ID_TIMER,100,NULL
ret
.elseif message == WM_TIMER
invoke GetCursorPos,addr pt
invoke GetPixel,hdcScreen,pt.x,pt.y
mov cr,eax
;invoke SetPixel,hdcScreen,pt.x,pt.y,0
mov eax,cr
.if (eax!=crLast)
mov eax,cr
mov crLast,eax
invoke InvalidateRect,hwnd,NULL,FALSE
.endif
ret
.elseif message == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
invoke GetClientRect,hwnd,addr rc
mov eax,cr
and eax,0FFh
push eax
mov eax,cr
and eax,0FF00h
shr eax,8
push eax
mov eax,cr
and eax,0FF0000h
shr eax,16
push eax
mov eax,CTEXT(" [ %02X %02X %02X] ")
push eax
lea eax,szBuffer
push eax
call wsprintf
invoke DrawText,hdc,addr szBuffer,-1,addr rc,DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hwnd,addr ps
ret
.elseif message == WM_DESTROY
invoke DeleteDC,hdcScreen
invoke KillTimer,hwnd,ID_TIMER
invoke PostQuitMessage,NULL
ret
.endif
invoke DefWindowProc,hwnd, message, wParam, lParam
ret
WndProc endp
END START
程序运行结果(可惜看不到鼠标)
WHATCLR在WinMain中做了一点与以往不同的事。因为WHATCLR的窗口只需要显示十六进制RGB值那么大,所以它在CreateWindow函数中使用WS_BORDER窗口样式建立了一个不能改变大小的窗口。要计算窗口的大小,WHATCLR通过先呼叫CreateIC再呼叫GetSystemMetrics以取得用于显示的设备内容信息。计算好的窗口宽度和高度值被传递给CreateWindow。
WHATCLR的窗口消息处理程序在处理WM_CREATE消息处理期间,呼叫CreateDC建立了用于整个视讯显示的设备内容。这个设备内容在程序的生命周期内都有效。在处理WM_TIMER消息处理期间,程序取得目前鼠标光标位置的图素。在处理WM_PAINT消息处理期间显示RGB颜色。
您可能想知道,从CreateDC函数中取得的设备内容句柄是否能让您在屏幕的任意位置显示一些东西,而不光只是取得图素颜色。答案是可以的,一般而言,让一个应用程序在另一个程控的画面区域上画图是不好的,但在某些特殊情况下,这可能会非常有用。比如,有一种“桌面破坏”的小游戏,你可以使用锤子电锯等等,破坏你的桌面。不喜欢的时候,按下一个键,马上又恢复为以前的桌面。
--------------------------------------------------------------------------------
<<<上一篇 欢迎访问AoGo汇编小站:http://www.aogosoft.com/ 下一篇>>>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -