검색결과 리스트
어셈블리 강좌에 해당되는 글 5건
글
튜토리얼 7: Mouse Input
이번 시간에는 윈도우 프로시져에서 어떻게 마우스 입력을 받고 응답하는지 배워 봅시다. 예제 프로그램에서는 왼쪽 마우스 클릭을 기다리고 있고, 클라이언트 영역에 클릭을 하면 바로 그 위치에 문자열을 출력할 것입니다.
Theory:
키보드 입려처럼, Windows 는 각각의 윈도우에 상대적인 마우스 움직임을 감지하고 이를 알려줄 것입니다. 왼쪽 오른쪽 마우스 클릭과 같은 동작은 물론, 윈도우 위에서의 마우스 커서의 움직임, 더블클릭 모두를 의미합니다. 키보드의 경우에는 입력 포커스를 가지고 있는 윈도우에만 메세지가 보내졌지만, 마우스 메세지의 경우에는 마우스 커서 아래의 윈도우, 활성화 되어 있는 윈도우, 비활성화 된 윈도우 등에 상관없이 모두에게 전달됩니다. 게다가 클라이언트 영역이 아닌 곳까지 모두 마우스 메세지가 전달됩니다. 하지만 대부분의 경우, 우린 이런 것들을 무시할 수 있습니다. 즉, 클라이언트 영역과 관련된 것들에만 초점을 둘 수 있습니다.
각 마우스 버튼에는 다음과 같이 두가지 메세지가 있습니다. : WM_LBUTTONDOWN, WM_RBUTTONDOWN 그리고 WM_LBUTTONUP, WM_RBUTTONUP. 버튼이 세개인 마우스를 위한 WM_MBUTTONDOWN 과 WM_MBUTTTONUP 도 있습니다. 마우스 커서가 클라이언트 영역 위를 움직이고 있을 때, Windows 는 WM_MOUSEMOVE 메세지를 마우스 커서 아래에 있는 윈도우에게 전달합니다.
만약 윈도우 클래스가 CS_DBLCLKS 스타일 플래그를 가지고 있다면, 윈도우는 WM_LBUTTONDBCLK, WM_RBUTTONDBCLK 메세지를 받을 수 있습니다. 플래그가 없다며 윈도우는 마우스 버튼 업, 다운 메세지를 받게 됩니다.
이런 모든 메세지들은 lParam 안에 마우스의 위치 정보를 가지고 있습니다. low word(역주: 하위 16비트)에는 윈도우 클라이언트 영역의 좌측 상단을 기준으로 한 x 좌표가, high word(역주: 상위 16비트)에는 y 좌표가 들어있습니다. 그리고 wParam 에는 마우스 버튼, Shift, Ctrl 키의 상태에 대한 정보가 들어 있습니다.
Example:
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MouseClick db 0 ; 0=no click yet
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hitpoint POINT <>
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analysis:
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
POINT STRUCT
x dd ?
y dd ?
POINT ENDS
그리고 적어도 한번 클라이언트 영역에 왼쪽 마우스 클릭이 있었다는 뜻으로, MouseClick 플래그를 TRUE 로 세팅합니다.
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
x 좌표값이 lParam 의 low word(역주: 하위 16비트) 안에 있는데, POINT 구조체 안에서 다루는 좌표 정보의 크기가 32비트이므로 hitpoint.x 값을 저장하기 전에 미리 eax 의 high word(역주 : 상위 16비트)를 0 으로 만들어야 합니다.
shr eax,16
mov hitpoint.y,eax
y 좌표값이 lParam 의 high word(역주: 상위 16비트)안에 들어있기 때문에, hitpoint.y 값을 저장하기 전에 eax 의 low word(역주: 하위 16비트)를 0으로 만들어야 하는데, 이는 eax 를 오른쪽으로 16비트 시프트 하면 해결됩니다.
마우스 좌표 정보를 저장한 뒤에는, MouseClick 플래그를 TRUE 로 세팅하여 WM_PAINT 섹션에 있는 코드에서 적어도 한번 클라이언트 영역이 클릭됬음을 인지하고, 클릭 된 위치에 문자열을 그릴 수 있도록 합니다. 그 다음 InvalidRect 함수를 호출하여 클라이언트 영역 전체가 강제적으로 다시 그려지도록 합니다.
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
WM_PAINT 섹션 안에 있는 문자열을 그리는 코드는 반드시 MouseClick 플래그가 TRUE 인지 체크를 해야 합니다. 그래야지 윈도우가 처음 만들어 졌을 때 받게 되는 WM_PAINT 메세지에서 마우스 클릭이 아직 없었음을 알고 문자열을 그리는 클라이언트 영역에 그리는 작업을 하지 않게 됩니다. MouseClick 을 FALSE 로 초기화 하고, 실제로 마우스 클릭이 발생 되었을 때 TRUE 로 변경합니다.
적어도 한번 마우스 클릭을 하게 되면, 클라이언트 영역의 클릭한 위치에 문자열을 그립니다. 여기에서 화면에 그려야 하는 문자열의 길이를 구하기 위해 lstrlen 함수를 사용하고, 그 값을 TextOut 함수의 마지막 파라미터로 사용하는 것에 주목하시기 바랍니다.
'Microsoft > MASM32' 카테고리의 다른 글
Visual Studio 2013 에서 MASM32 개발하기 (2) | 2014.05.07 |
---|---|
전역후킹(Global Hooking) (0) | 2011.01.23 |
[MASM 강좌] 튜토리얼 6 : Keyboard Input (0) | 2010.12.12 |
[MASM 강좌] 튜토리얼 5 : More about Text (0) | 2010.12.12 |
[MASM 강좌] 튜토리얼 4 : Painting with Text (0) | 2010.11.29 |
RECENT COMMENT