📄 见招拆招windows程序设计(六) .txt
字号:
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;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
WndProc proc hwnd:DWORD,message:DWORD,wParam :DWORD,lParam :DWORD
LOCAL hdc :HDC
LOCAL x,y :DWORD
LOCAL ps :PAINTSTRUCT
LOCAL rect :RECT
LOCAL xcx,ycy,x1cx,y1cy:DWORD
.if message == WM_SIZE
mov eax,lParam ;cxBlock = LOWORD (lParam) / DIVISIONS
shl eax,16
shr eax,16
mov ecx,DIVISIONS
div ecx
mov cxBlock,eax
xor edx,edx
mov eax,lParam ;cyBlock = HIWORD (lParam) / DIVISIONS
shr eax,16
mov ecx,DIVISIONS
div ecx
mov cyBlock,eax
mov ebx,eax
ret
.elseif message == WM_LBUTTONDOWN
xor edx,edx
mov eax,lParam
shl eax,16
shr eax,16
mov ecx,cxBlock
div ecx
mov x,eax
xor edx,edx
mov eax,lParam
shr eax,16
mov ecx,cyBlock
div ecx
mov y,eax
mov eax,x
mov ebx,y
lea esi,fState
.if (eax<DIVISIONS)&&(ebx<DIVISIONS)
mov eax,y
mov ecx,DIVISIONS
mul ecx
add esi,eax
add esi,x
xor BYTE ptr [esi],1
;RECT STRUCT
; left dd ?
; top dd ?
; right dd ?
; bottom dd ?
;RECT ENDS
lea esi,rect
mov eax,x
mov ecx,cxBlock
mul ecx
mov [esi],eax
mov eax,y
mov ecx,cyBlock
mul ecx
mov [esi+4],eax
mov eax,x
inc eax
mov ecx,cxBlock
mul ecx
mov [esi+8],eax
mov eax,y
inc eax
mov ecx,cyBlock
mul ecx
mov [esi+12],eax
invoke InvalidateRect,hwnd,addr rect,FALSE
.elseif
invoke MessageBeep,0
.endif
ret
.elseif message == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax
xor eax,eax
mov x,eax
Loopx:
xor eax,eax
mov y,eax
Loopy:
mov eax,y
inc eax
mov ecx,cyBlock
mul ecx
mov y1cy,eax
push eax
mov eax,x
inc eax
mov ecx,cxBlock
mul ecx
mov x1cx,eax
push eax
mov eax,y
mov ecx,cyBlock
mul ecx
mov ycy,eax
push eax
mov eax,x
mov ecx,cxBlock
mul ecx
mov xcx,eax
push eax
push hdc
call Rectangle
lea esi,fState
mov eax,y
mov ecx,DIVISIONS
mul ecx
add esi,eax
add esi,x
mov al,[esi]
.if (al!=0)
;invoke wsprintf,addr szBuffer,CTEXT("[%d][%d][%d][%d]") ,x,y,ecx,ebx
;invoke MessageBox,hwnd,addr szBuffer,NULL,NULL
invoke MoveToEx,hdc,xcx,ycy,NULL
invoke LineTo,hdc,x1cx,y1cy
invoke MoveToEx,hdc,xcx,y1cy,NULL
invoke LineTo,hdc,x1cx,ycy
.endif
inc y
mov eax,y
cmp eax,DIVISIONS
jb Loopy
inc x
mov eax,x
cmp eax,DIVISIONS
jb Loopx
invoke EndPaint,hwnd, addr ps
ret
.elseif message == WM_DESTROY
invoke PostQuitMessage,NULL
.endif
UseDefWindowProc:
invoke DefWindowProc,hwnd, message, wParam, lParam
ret
WndProc endp
END START
图7-3是CHECKER1的显示。程序画的25个矩形的宽度和高度均相同。这些宽度和高度保存在cxBlock和cyBlock中,当显示区域大小发生改变时,将重新对这些值进行计算。WM_LBUTTONDOWN处理过程使用鼠标坐标来确定在哪个矩形中按下了键,它在fState数组中标志目前矩形的状态,并使该矩形区域失效,从而产生WM_PAINT消息。
图7-3 CHECKER1的屏幕显示
如果显示区域的宽度和高度不能被5整除,那么在显示区域的左边和下边将有一小条区域不能被矩形所覆盖。对于错误情况,CHECKER1通过呼叫MessageBeep响应此区域中的鼠标按键操作。
当CHECKER1收到WM_PAINT消息时,它通过GDI的Rectangle函数来重新绘制显示区域。如果设定了fState值,那么CHECKER1将使用MoveToEx和LineTo函数来绘制两条直线。在处理WM_PAINT期间,CHECKER1在重新绘制之前并不检查每个矩形区域的有效性,尽管它可以这样做。检查有效性的一种方法是在循环中为每个矩形块建立RECT结构(使用与WM_LBUTTONDOWN处理程序中相同的公式),并使用IntersectRect函数检查它是否与无效矩形(ps.rcPaint)相交。
使用键盘仿真鼠标
CHECKER1只能在装有鼠标情况下才可执行。下面我们在程序中加入键盘接口,就如同前面章节中对SYSMETS程序所做的那样。不过,即使在一个使用鼠标光标作为指向用途的程序中加入键盘接口,我们还是必须处理鼠标光标的移动和显示问题。
即使没有安装鼠标,Windows仍然可以显示一个鼠标光标。Windows为这个光标保存了一个「显示计数」。如果安装了鼠标,显示计数会被初始化为0;否则,显示计数会被初始化为-1。只有在显示计数非负时才显示鼠标光标。要增加显示计数,您可以呼叫:
ShowCursor (TRUE) ;
要减少显示计数,可以呼叫:
ShowCursor (FALSE) ;
您在使用ShowCursor之前,不需要确定是否安装了鼠标。如果您想显示鼠标光标,而不管鼠标存在与否,那么只需呼叫ShowCursor来增加显示计数。增加一次显示计数之后,如果没有安装鼠标则减少它以隐藏光标,如果安装了鼠标,则保留其显示。
即使没有安装鼠标,Windows也保留了鼠标目前的位置。如果没有安装鼠标,而您又显示鼠标光标,光标就可能出现在显示器的任意位置,直到您确实移动了它。要获得光标的位置,可以呼叫:
GetCursorPos (&pt) ;
其中pt是POINT结构。函数使用鼠标的x和y坐标来填入POINT字段。要设定光标位置,可以使用:
SetCursorPos (x, y) ;
在这两种情况下,x和y都是屏幕坐标,而不是显示区域坐标(这是很明显的,因为这些函数没有要求hwnd参数)。前面已经提到过,呼叫ScreenToClient和ClientToScreen就能做到屏幕坐标与客户坐标的相互转换。
如果您在处理鼠标消息并转换显示区域坐标时呼叫GetCursorPos ,这些坐标可能与鼠标消息的lParam参数中的坐标稍微有些不同。从GetCursorPos传回的坐标表示鼠标目前的位置。lParam中的坐标则是产生消息时鼠标的位置。
您或许想写一个键盘处理程序:使用键盘方向键来移动鼠标光标,使用Spacebar和Enter键来仿真鼠标按键。您肯定不希望每次按键只是将鼠标光标移动一个图素,如果这样做,当要把鼠标光标从显示器的一边移动到另一边时,会使用者在很长一段时间内都要按住同一个方向键。
如果您需要实作鼠标光标的键盘接口,并保持光标的精确定位能力,那么您可以采用下面的方式来处理按键消息:当按下方向键时,一开始鼠标光标移动较慢,但随后会加快。您也许还记得WM_KEYDOWN消息中的lParam参数标志着按键消息是否是重复活动的结果,这就是此参数的一个重要应用。
在CHECKER中加入键盘接口
程序7-3所示的CHECKER2程序,除了包括键盘接口外,和CHECKER1是一样的,您可以使用左、右、上和下方向键在25个矩形之间移动光标。Home键把光标移动到矩形的左上角, End键把光标移动到矩形的右下角。Spacebar和Enter键都能切换X标记。
程序7-3 CHECKER2
CHECKER2.ASM
;MASMPlus 代码模板 - 普通的 Windows 程序代码
.386
.Model Flat, StdCall
Option Casemap :None
Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc
Include libc.inc
includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
IncludeLib msvcrt.lib
include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
DIVISIONS equ 5
.DATA
szAppName DB "Checker2",0
.DATA?
hInstance DD ?
fState DB DIVISIONS*DIVISIONS dup (?)
cxBlock DD ?
cyBlock DD ?
szBuffer db 100 dup (?)
.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
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
invoke CreateWindowEx,
NULL,
ADDR szAppName, ;window class name
CTXT("Checker2 Mouse Hit-Test Demo"), ;window caption
WS_OVERLAPPEDWINDOW, ;window style
CW_USEDEFAULT, ;initial x position
CW_USEDEFAULT, ;initial y position
CW_USEDEFAULT, ;initial x size
CW_USEDEFAULT, ;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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -