Baja el ejemplo aquí.
Windows provee algunas clases de ventana predefinidas que podemos usar satisfactoriamente dentro de nuestros programas. Muchas veces las usamos como componentes de una caja de diálogo por lo que ellas usualmente son llamadas controles de ventanas hijas. Los controles de ventanas hijas procesan sus propios mensajes de teclado y de ratón y notifican a las ventanas padres cuando sus estados han cambiado. Ellos liberan al programador de enormes cargas, así que deberías usarlas cada vez que sea posible. En este tutorial las pongo sobre una ventana normal para demostrar cómo puedes crearlas y usarlas, pero en realidad deberías ponerlas en una caja de diálogo.
Ejemplos de clases de ventanas predefinidas son el botón, la caja de lista [listbox], la caja de chequeo [checkbox], botón de radio [radio button], edición [edit] etc.
Con el fin de usar el control de vemtana hija, debes crearla con CreateWindow o CreateWindowEx. Nota que no tienes que registrar la clase de ventana puesto que Windows lo hace por tí. El parámetro nombre de la clase DEBE ser el nombre de la clase predefinida. Es decir, si quieres crear un botón, debes especificar "button" como nombre de la clase en CreateWindowEx. Los otros parámetros que debes llenar son la agarradera o manejador [handle] de la ventana padre y el ID del control. El ID del control debe ser único entre los controles. El ID del control es el ID de ese control. Lo usas para diferenciar entre controles.
Después de que el control fue creado, enviará mensajes de notificación a la ventana padre cuando su estado cambie. Normalmente, creas las ventanas hijas durante el mensaje WM_CREATE de la ventana padre. La ventana hija envía mensajes WM_COMMAND a la ventana padre con su ID de control en la palabra baja de wParam, el código de notificación en la palabra alta de wParam, y su manejador de ventana en lParam. Cada conrtol de ventana hija tiene su propio código de notificación, así que debes revisar la referencia de la API de Win32 para más información.
También la ventana padre puede enviar órdenes o comandos a la ventana hija, llamando a la función SendMessage. Esta función envía el mensaje especificado acompañado de otrops valores en wParam y lParam a la ventana especificada por el manejador de ventana. Es una función extremadamente útil, ya que puede enviar mensajes a cualquier ventana que conozcas su manejador.
Así que después de crear ventanas hijas, la ventana padre debe procesar los mensajes WM_COMMAND para poder recibir códigos de notificación desde las ventanas hijas.
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include
\masm32\include\windows.inc
include
\masm32\include\user32.inc
include
\masm32\include\kernel32.inc
includelib
\masm32\lib\user32.lib
includelib
\masm32\lib\kernel32.lib
.data
ClassName
db "SimpleWinClass",0
AppName
db "Our First Window",0
MenuName
db "FirstMenu",0
ButtonClassName
db "button",0
ButtonText
db "My First Button",0
EditClassName
db "edit",0
TestString
db "Wow! I'm in an edit box now",0
.data?
hInstance
HINSTANCE ?
CommandLine
LPSTR ?
hwndButton
HWND ?
hwndEdit
HWND ?
buffer db
512 dup(?) ; buffer para almacenar
el texto recuperado desde la ventana de edición
.const
ButtonID
equ 1
; El ID del control botón [button]
EditID equ
2
; El ID del control de edición [edit]
IDM_HELLO
equ 1
IDM_CLEAR
equ 2
IDM_GETTEXT
equ 3
IDM_EXIT
equ 4
.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_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
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_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT,\
300,200,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
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50,35,200,25,hWnd,8,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc
endp
end
start
Creamos los controles cuando procesamos el mensaje WM_CREATE. Llamamos a CreateWindowEx con el estilo de ventana extra, WS_EX_CLIENTEDGE, el cual hace que la ventana cliente se vea con un borde sobreado. El nombre de cada control es un nombre predefinido, "edit" para el control de edición, "button" para el control botón. Luego especificamos los estilos de ventanas hijas. Cada control tiene estilos extras además de los estilos de ventana normal. Por ejemplo, los estilos de botón llevan el prefijo "BS_" para "button style", los estilos del control de edición llevan "ES_" para "edit style". Tienes que revisar estos estilos en la referenicia de la API de Win32. Nota que pones un ID de control en lugar del manejador del menú. Esto no causa problemas ya que el control de una ventana hija no puede tener menú.
Después de crear cada control, guardamos su manejador en una variable para su futuro uso.
Se llama a SetFocus para dar fooco de entrada a la caja de edición de manera que el usuario pueda tipear texto dentro de ella immediatamente.
Ahora la parte realmente exitante. Todo control de ventana hija envía una notificación a su ventana padre con WM_COMMAND.
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
Recuerda que
también un menu envía mensajes WM_COMMAND para notificar a su
ventana sobre su estado. ¿Cómo puedes diferenciar si los mensajes
WM_COMMAND son originados desde un menú o desde un control? He aquí
la respuesta:
Palabra baja de wParam | Palabra alta de wParam | lParam | |
Menu | Menu ID | 0 | 0 |
Control | Control ID | Código de notificación | Manjeador de ventana hija |
Como puedes ver, hay que chequear lParam. Si es cero, el mensaje WM_COMMAND actual es de un menú.No puedes usar wParam para diferenciar entre un menú y un control porque el ID del menú y el ID del control pueden ser idénticos y el código de notificación puede ser cero.
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
Puedes poner una cadena de texto dentro de una caja de edición llamando a SetWindowText. Limpias el contenido de la caja de edición llamando a SetWindowText con NULL. SetWindowText es una función de la API de propósito general. Puedes usar SetWindowText para cambiar el encabezamiento o título [caption] de una ventana o el texto sobre un botón.
Para obtener el texto en una caja de edición, usas GetWindowText.
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
El fragmento de código de arriba tiene que ver con la condición de si el usuario presiona el botón. Primero, chequea la palabra baja de wParam para ver si el ID del control coincide con el del botón. Si es así, chequea la palabra alta de wParam para ver si es el código de notificación BN_CLICKED que se envía cuando el botón es pulsado.
La parte interesante es después que el código de notificación es BN_CLICKED. Queremos obtener el texto de la caja de edición y desplegarlo en la caja de edición. Podemos duplicar el código en la sección IDM_GETTEXT de arriba pero no tiene sentido. Si de alguna manera podemos enviar un mensaje WM_COMMAND con el valor IDM_GETTEXT en la palabra baja de wParam a nuestro procedimiento de ventana, podemos evitar duplicación de código y simplificar nuestro programa. La función SendMessage es la respuesta. Esta función envía cualquier mensaje a cualquier ventana con cualquieras wParam y lParam que desiemos. Así que e nvez de duplicar código, llamamos a SendMessage con el manejador de ventana padre, WM_COMMAND, IDM_GETTEXT, y 0. Esto tiene un efecto idéntico que seleccionar el elemento "Get Text" de nuestro menú. El procedimiento de ventana no percibirá ninguna diferencia entre los dos.
Deberías usar estas técnicas en la medida de lo posible para que tu código sea más organizado.
Por último,
no olvides poner la función TranslateMessage en el bucle de mensajes,
puesto que, como debes tipear algún texto en la caja de edición,
tu programa debe traducir la entrada cruda del teclado a texto que pueda ser
leído. Si omites esta función, no serás capáz de
editar nada en la caja de edición.
[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