Tutorial 6: Entrada del teclado

Aprenderemos como un programa de Windows recibe entrada de teclado.

Baja el ejemplo aquí.

Teoría:

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.
 

Ejemplo:

.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
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
 

Analysis:


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.


Índice

Siguiente

[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

www.000webhost.com