Aprenderemos como un programa de Windows recibe entrada de teclado.
Baja el ejemplo aquí.
Como normalmente sólo hay un teclado para cada PC, todos los programas de Windows deben compartirlo entre sí. Windows es responsable de enviar los golpes de tecla a la ventana que tiene el foco de entrada.
Aunque pueden haber varias ventanas en el monitor, sólo una de ellas tiene el foco de entrada. La ventana que tiene el foco de entrada es la única que puede recibir los golpes de tecla. Puedes diferenciar la ventana que tiene el foco de entrada de las otras ventanas observando la barra de título. La barra de título del programa que tiene el foco está iluminada.
Realmente, hay dos tipos principales de mensajes de teclado, dependiendo de tu punto de vista sobre el teclado. Puedes ver el teclado como una colección de teclas. En este caso, si presionas una tecla, Windows envía un mensaje WM_KEYDOWN a la ventana que tiene el foco de entrada, que notifica que una tecla ha sido presionada. Cuando sueltas la tecla, Windows envía un mensaje WM_KEYUP. Tú tratas a las teclas como si fueran botones.
Otra manera
de ver el teclado es como un dispositivo de entrada de caracteres. Cuando presionas
"una" tecla, Windows envía un mensaje WM_CHAR a la ventana que tiene
el foco de entrada, diciéndole que el usuario envía "un" caracter
a ella. En realidad, Windows envía mensajes WM_KEYDOWN y WM_KEYUP a la
ventana que tiene el foco de entrada y esos mensajes serán traducidos
a mensajes WM_CHAR por una llamada a TranslateMessage. El procedimiento de ventana
puede decidir si procesa los tres mensajes o sólo los mensajes que interesan.
Muchas veces, podrás ignorar WM_KEYDOWN y WM_KEYUP ya que la función
TranslateMessage en el bucle de mensajes traduce los mensajes WM_KEYDOWN y WM_KEYUP
a mensajes WM_CHAR. En este tutorial nos concentraremos en WM_CHAR.
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
char WPARAM
20h
; el caracter que el programa recibe del teclado
.data?
hInstance
HINSTANCE ?
CommandLine
LPSTR ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
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 TranslateMessage, ADDR msg
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_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke TextOut,hdc,0,0,ADDR char,1
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc
endp
end
start
char WPARAM
20h
; el caracter que el programa recibe del teclado
Esta es la variable que guardará el caracter recibido del teclado. Como el caracter es enviado en WPARAM del procedimiento de ventana, por simplicidad definimos los tipos de variables como WPARAM. El valor inicial es 20h o el espacio, ya que cuando nuestra ventana refresque su área cliente por primera vez, ahí no habrá caracter de entrada. Así que preferimos desplegar el espacio.
.ELSEIF uMsg==WM_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
Esto es agregado al manejador del mensaje WM_CHAR en el procedimiento de ventana. Pone el caracter dentro de la variable llamada "char" y luego llama a InvalidateRect. InvalidateRect hace que le rectángulo específico en el área cliente quede invalidado para forzar a Windows para que envíe el mensaje WM_PAINT al procedimiento de ventana. Su sintaxis es como sigue:
InvalidateRect
proto hWnd:HWND,\
lpRect:DWORD,\
bErase:DWORD
lpRect
es un opuntero al rectángulo en el área clienete que queremos
declarar inválida. Si este parámetro es nulo, toda el área
cliente será marcada como inválida.
bErase
es una bandera que dice a Windows si necesita borrar el fondo. Su ventana es
TRUE, luego Windows borrará el fondo del rectángulo invalidado
cuando se llama a BeginPaint.
Así que la estrategia que usamos aquí es: almacenamos toda la información necesaria involucrada en la acción de pintar el área cliente y generar el mensaje WM_PAINT para pintar el área cliente. Por supuesto, el código en la sección WM_PAINT debe saber de antemano qué se espera de ella. Esto parece una manera indirecta de hacer las cosas, pero así es como lo hace Windows.
Realmente podemos pintar el área cliente durante el proceso del mensaje WM_CHAR llamando el par de funciones GetDC y ReleaseDC. No hay problema. Pero lo gracioso comienza cuando nuestra ventana necesita volver a pintar su área cliente. Como el código que pinta el caracter está en la sección WM_CHAR, el procedimiento de ventana no será capaz de pintar nuestro caracter en el area cliente. Así que la linea de abajo es: poner todos el código y los datos necesarios para que realicen la acción de pintar en WM_PAINT. Puedes enviar el mensaje WM_PAINT desde cualquier lugar de tu código cada vez que quieras volver a pintar el área cliente.
invoke TextOut,hdc,0,0,ADDR char,1
Cuando se
llama a InvalidateRect, envía un mensaje WM_PAINT de regreso al procedimiento
de ventana. De esta manera es llamado el código en la sección
WM_PAINT. Llama a BeginPaint como es usual para obtener el manejador al contexto
del dispositivo y luego llama a TextOut que dibuja nuestro caracter en el area
cliente en x=0, y=0. Cuando corres el programa y presionas cualquier tecla,
verás un "eco" [echo] del caracter en la esquina izquierda
superior del área cliente de la ventana. Y cuando la ventana sea minimizada,
al ser maximizada de nuevo tendrá todavía el caracter ahí
ya que todo el código y los datos esenciales para volver a pintar son
todos activados en la sección WM_PAINT.
[Iczelion's Win32 Assembly HomePage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por: n u M I T_o r