Tutorial 20: Subclasificación de Ventanas

En este tutorial, aprenderemos acerca de la subclasificación de ventanas, qué es y como aprovecharla.
Bajar el ejemplo aquí.

Teoría:

Si programas por un tiempo en Windows encontrarás casos donde tu ventana tiene CASI todos los atributos que deseas pero no todos. ¿Haz encontrado una situación donde deseas una clase especial de control de edición que filtre algunos caracteres indeseables? Lo más directo que se puede hacer es codificar tu propia ventana. Pero relamente es un trabajo duro y consume tiempo. La subclasificación de Windows al rescate.

En pocas palabras, la subclasificación de ventanas te permite hacerte cargo de la ventana subclasificada. Tienes el control absoluto sobre ella. Tomemos un ejemplo para clarificar más. Supongamos que necesitas una caja de texto que acepte sólo cadenas en hexadecimal. Si usas un control de edición simple, tienes que decir algo cuando el usuario transcribe algo diferente a números hexadecimales dentro de la caja de texto, es decir. si el usuario escribe "zb+q" dentro de la caja de texto, no puedes hacer nada con ello, excepto rechazar toda la cadena de texto. Esto al menos carece de profesionalidad. En esencia, necesitas la habilidad de examinar cada caracter que el usuario escribió dentro de la caja de texto en el momento que él lo transcribió.

Ahora examinaremos cómo hacerlo. Cuando el usuario tipea algo dentro de la caja de texto, Windows envía el mensaje WM_CHAR al procedimiento de ventana del control de edición. Este procedimiento de ventana reside dentro de Windows así que no podemos modificarlo. Pero podemos redirigir el flujo de mensajes a nuestro propio procedimiento de ventana. Así que nuestro procedimiento de ventana obtendrá primero un impacto [shot] de cualquier mensaje de Windows antes de que sea enviado al control de edición. Si nuestro procedimeinto de ventana resuelve actuar sobre el mensaje, puede aprovechar ahora para hacerlo. Pero si no desea manejar el mensaje, lo pasa al procedimiento de ventana principal. De esta manera, nuestro procedimiento de ventana se inserta dentro de Windows y del control de edición. Veamos el siguiente flujo:

Ahora ponemos nuestra atención en cómo subclasificar una ventana. Nota que la subclasificación no está limitada a los controles, puede ser usada con cualquier ventana. Pensemos cómo Windows llega a tener conocimiento sobre dónde residen los procedimientos de ventana de los controles de edición. Una pista?......el miembro lpfnWndProc de la estructura WNDCLASSEX. Si podemos reemplazar este miembro con la dirección de nuestro procedimiento de ventana, Windows enviará más bien mensajes a nuestro procedimiento de ventana.

Podemos hacer esto llamando a SetWindowLong.

hWnd = manejador de la ventana cuya estructura WNDCLASSEX será cambiada
nIndex == valor a cambiar.

dwNewLong = el valor de reemplazo.

Así que la tarea es fácil. Programamos un procedimiento de ventana que maneje los mensajes para el control de edición y luego llamamos a SetWindowLong con la bandera GWL_WNDPROC, pasando junto a ella la dirección de nuestro procedimento de ventana como tercer parámetro. Si la función tiene éxito, el valor de regreso es el valor previo del número entero especificado de 32-bit, en nuestro caso, la dirección del procedimiento de ventana original. Necesitamos almacenar este valor para usarlo dentro de nuestro procedimiento de ventana.

Recuerda que habrá algunos mensajes que no querrás manejar, los pasaremos al procedimiento de ventana original. Podemos hacerlo llamando a la función CallWindowProc.

lpPrevWndFunc = la dirección del procedimiento de ventana original.

Los restantes cuatro parámetros son pasados a nuestro procedimiento de ventana. Los pasamos justo con CallWindowProc.

Código Muestra:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName  db "SubclassWinClass",0
AppName    db "Subclassing Demo",0
EditClass  db "EDIT",0
Message  db "You pressed Enter in the text box!",0

.data?
hInstance  HINSTANCE ?
hwndEdit dd ?
OldWndProc dd ?

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, 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_APPWORKSPACE
    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,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
 WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,350,200,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    .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
    .if uMsg==WM_CREATE
        invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClass,NULL,\
            WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
            20,300,25,hWnd,NULL,\
            hInstance,NULL
        mov hwndEdit,eax
        invoke SetFocus,eax
        ;-----------------------------------------
        ; Subclass it!
        ;-----------------------------------------
        invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
        mov OldWndProc,eax
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp

EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    .if uMsg==WM_CHAR
        mov eax,wParam
        .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
            .if al>="a" && al<="f"
                sub al,20h
            .endif
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
            ret
        .endif
    .elseif uMsg==WM_KEYDOWN
        mov eax,wParam
        .if al==VK_RETURN
            invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SetFocus,hEdit
        .else
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
            ret
        .endif
    .else
        invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
EditWndProc endp
end start

Análisis:

Después de que el control de ventana es creado, lo subclasificamos llamando a SetWindowLong, reemplazando la dirección principal del procedimiento de ventana con la dirección del nuestro. Observa que almacenamos la dirección del procedimiento principal para usarlo luego con CallWindowProc. Nota también que EditWndProc es un procedimiento de ventana común y corriente.

Dentro de EditWndProc, filtramos los mensajes WM_CHAR. Si el caracter está entre 0-9 o a-f, lo aceptamos pasándolo con el mensaje al procedimiento de ventana original. Si el carácter está en minúscula, lo convertimos en mayúscula agregándole 20h. Nota que si el caracter no es el único que esperamos, lo descartamos. No lo pasamos al procedimiento de ventana original. Así que cuando el usuario escribe algo distinto a 0-9 o a a-f, el caracter no aparecerá en la ventana de edición.

Ahora quiero demostrar el poder de la subclasificación atrapando la tecla ENTER. EditWndProc chequea el mensaje WM_KEYDOWN para verificar si es VK_RETURN (la tecla Enter). Si lo es, despliega una caja de mensaje que dice "You pressed the Enter key in the text box!". Si no es una tecla Enter, pasa el mensaje al procedimiento de ventana original.

Puedes usar la subclasificación de ventanas para tomar el control sobre otras ventanas. Es una de las técnicas más poderosas que deberías tener en tu arsenal.


Index

Next

[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