Proyectos visuales - El modo potente de Easy Code






Cuando se elige la opción Archivo ejecutable visual, se crea un proyecto visual por defecto llamado Project1. Este tipo de proyecto le dice automáticamente a Easy Code que asocie su librería visual, que es necesaria para todas las funcionalidades visuales.

Hay dos maneras de enlazar la librería visual, estática o dinámica, y hay que indicar una de ellas en las propiedades del proyecto, disponibles a través del menú "Proyecto-->Propiedades". El modo dinámico generará la aplicación con la librería visual necesaria en un archivo aparte (ECDllGo.dll o ECDllGou.dll) que debe distribuirse con la aplicación, mientras que el modo estático incluirá la librería visual necesaria (ECStcGo.lib o ECStcGou.ib) dentro del archivo ejecutable.

El cuadro de verificación Manifest.xml indica si se activarán los nuevos "comon controls" que se encuentran disponibles desde Windows XP y posteriores. Para más información, consulte el tópico Incluyendo un Manifiesto en el proyecto.


RECURSOS VISUALES DE EASY CODE

El modo visual de Easy Code divide los objetos en dos grupos: Ventanas y Controles.

Ventanas

Las ventanas son contenedores, es decir, pueden tener controles hijo en su interior. Easy Code maneja tres tipos de objetos ventana:

Window: Ventanas corrientes utilizadas en pequeñas aplicaciones como ventanas principales. Un objeto Window puede ser modal (si no es la ventana principal), una ventana MDI hija, o una ventana no modal.

DialogBox: Utilizadas principalmente como ventanas modales para seleccionar o modificar opciones de la ventana principal de la aplicación que las crea. Un objeto DialogBox también puede ser no modal y utilizarse como ventana principal de una aplicación.

MDIWindow: Utilizadas como ventanas principales de las aplicaciones MDI. Estas ventanas pueden tener otras ventanas de tipo MDI hija en su interior. Un proyecto visual de Easy Code sólamente puede tener un objeto MDIWindow.

Controles

Los controles son pequeñas ventanas situadas dentro de los objetos Window y DialogBox. También pueden ponerse dentro de los dos tipos de controles contenedores, Group y Picture. Easy Code maneja varios objetos control (para una lista de controles disponibles vea Objetos control).


NOTA
: No hace falta que inicialice los "Common Controls", función de la API "InitCommonControlsEx", puesto que Easy Code los inicializa internamente dependiendo de la opción Common controls de las propiedades del proyecto.



VENTANA PROPIETARIA ("OWNER WINDOW")

La ventana de mas alto nivel de un objeto control, incluso si es hijo de otro control que sea contenedor (como Group o Picture), es el objeto Window, MDIWindow o DialogBox que los contiene a todos ellos, y que recibe el nombre de "Owner window". Cuando una "owner window" recibe el mensaje WM_CREATE, todos los controles hijo están creados y disponibles. Si la "owner window" es un objeto MDIWindow, su ventana MDIClient también está creada, y su "handle" se puede obtener llamando al método de Easy Code GetMDIClient (vea Aplicaciones MDI).

NOTA: En todo momento puede saber cual es la "owner window" de un objeto control llamando al método de Easy Code GetOwnerWindow.

Las librerías visuales de Easy Code, ECStcGo.lib o ECStcGou.lib para enlace estático y ECDllGo.dll o ECDllGou.dll para enlace dinámico, están hechas en lenguaje ensamblador y controlan el funcionamiento de todos los objetos, ocupándose de la navegación entre los controles a través de la tecla <Tab>, teclas rápidas de los menús, apariencia, texto y colores de los objetos, etc.. Además de estas librerías, el modo visual de Easy Code siempre incluye los siguientes archivos:

Archivos Include:

windows.inc

Archivos de enlace:

gdi32.dll
kernel32.dll
user32.dll
advapi32.dll


Por lo tanto, no necesita incluir ninguno de estos archivos en su proyecto, puesto que si lo hace serán ignorados. La mayoría de proyectos sólo necesitan estos archivos, o incluso alguno menos.



PROCEDIMIENTOS DE VENTANA

Todos los objetos de un proyecto, ventanas y controles, tienen propiedades (vea Propiedades de los objetos) que pueden modificar su apariencia y funcionamiento. Una de las propiedades mas importantes es Name, puesto que identifica al objeto dentro de su "owner window" (Window, MDIWindow o DialogBox) y forma parte del nombre de su procedimiento de ventana. Easy Code escribe la correspondiente plantilla de código para las ventanas y controles conforme se van agregando al proyecto. Dentro de estos procedimientos puede interceptar los mensajes que le interesen y escribir el código oportuno. A la hora de compilar, Easy Code comprueba que haya el correspondiente procedimiento de ventana para cada uno de los objetos Window, MDWindow y DialogBox del proyecto. Si alguno de ellos no está, o su nombre de procedimiento es incorrecto, se genera un error. Todos los procedimientos de objetos ventana DEBEN existir a la hora de compilar.

¿Cómo sabe Easy Code el nombre correcto de cada procedimiento de ventana? Para saberlo se basa en una simple regla que utiliza el nombre del objeto mas la palabra "Procedure". Es decir, los procedimientos de los objetos Window, MDIWindow o DialogBox, DEBEN tener un nombre que se forma con el propio nombre del objeto (indicado por su propiedad Name) mas la palabra "Procedure". Por ejemplo, un objeto ventana llamado wndMain debe tener el nombre de procedimiento siguiente:

wndMainProcedure (respetando mayúsculas y minúsculas)

Por otra parte, los procedimientos de objetos Window, MDIWindow o DialogBox también DEBEN tener los clásicos cuatro parámetros:

hWnd, uMsg, wParam, lParam


Por lo tanto, el procedimiento (o Frame) de un objeto ventana llamado wndMain será mas o menos así:

wndMainProcedure Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_CREATE
Jne >
Call OnCreate
Ret
: Cmp D[uMsg], WM_CLOSE
Jne >L2
Call OnClose
Ret
L2: Return (FALSE)
EndF
OnCreate:
UseData wndMainProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;
Return (FALSE)
EndU
OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU


Este es el código mínimo que necesita el procedimiento de un objeto ventana, pero no hace nada mas que crear el objeto y destruirlo (analizaremos el mensaje WM_CLOSE mas adelante). Return es una macro de Easy Code (vea Macros de Easy Code), que hace que el valor de retorno sea mas rápido y mas claro.

Para cada mensaje que desee procesar, añada el código correspondiente. Por ejemplo, para procesar el mensaje WM_SIZE añada el código siguiente:

wndMainProcedure Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_CREATE
Jne >
Call OnCreate
Ret
: Cmp D[uMsg], WM_SIZE
Jne >
Call OnSize
Ret
: Cmp D[uMsg], WM_CLOSE
Jne > L2
Call OnClose
Ret
L2: Return (FALSE)
EndF
OnCreate:
UseData wndMainProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;
Return (FALSE)
EndU
OnSize:
UseData wndMainProcedure
;=======================================;
; El código del mensaje WM_SIZE va aquí ;
;=======================================;
Return (TRUE)
EndU
OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU

Como puede ver, el procedimiento de ventana siempre devuelve FALSE o TRUE para funcionar correctamente. Cuando se devuelve TRUE (cualquier valor que no sea 0), no habrá ningún procesamiento mas para ese mensaje. Contrariamente, si el valor de retorno es FALSE, se llamará al procedimiento de ventana por defecto de Windows. Esta es la razón por la que el valor de retorno por defecto, al final del procedimiento, es FALSE. Todos los mensajes que usted no procese serán procesados por el procedimiento de ventana por defecto, que es parte de la API de Windows, para evitar resultados no deseados. Por lo tanto, no cambie nunca el valor de retorno por defecto FALSE y simplemente devuelva TRUE para todos aquellos mensajes que no desee que continuen siendo procesados. Esta regla, sin embargo, tiene las siguientes excepciones:

El mensaje WM_NCCREATE ignora el valor de retorno y continua la creación de la ventana.

El mensaje WM_CREATE debe devolver cualquier valor diferente de -1 para continuar la creación de la ventana. Es recomendable que ese valor de retorno sea TRUE o FALSE. Si es TRUE, el foco se asignará al primer control que pueda tenerlo (vea la propiedad TabOrder). La mayoría de las veces, este es el valor de retorno mas utilizado para que el foco se asigne al primer control disponible. Por otra parte, si asigna el foco a cualquier otro control mientras procesa este mensaje (rutina de la API SetFocus), debe devolver FALSE para que el foco quede asignado al control en cuestión. Finalmente, un valor de retorno de -1 destruirá la ventana antes de que sea visible.

Los mensajes WM_DESTROY y WM_NCDESTROY se envian antes de destruir el objeto para poder liberar los recursos utilizados ("clean up"), pero ignoran el valor de retorno y destruyen la ventana igualmente.



CREANDO UN NUEVO PROYECTO VISUAL

Cuando se crea un nuevo proyecto visual, se agrega automáticamente una ventana con el nombre de "Window1". Si no la cambia, ésta será la ventana principal ("main window"). Cualquiera de las ventanas del proyecto puede ser la ventana principal o de inicio, es decir, la ventana que aparece en primer lugar y que es la ventana principal de la aplicación (el objeto App siempre devuelve el "handle" de la ventana principal a través de su miembro Main). En este punto, puede diseñar tres tipos de aplicaciones:

Una aplicación básica con una ventana principal que es un objeto Window. Opcionalmente, puede tener otras ventanas (modales o no) como, por ejemplo, una ventana de configuración. Este es el proyecto creado por defecto y es como están hechas las aplicaciones de ejemplo File Shredder, MIDI Player, RGG y TabStrip.

Una aplicación MDI con una ventana principal MDI, un objeto MDIWindow, y tantas ventanas MDI hija como hagan falta. Las ventanas MDI hija son ventanas normales (objetos Window), pero con su propiedad MDIChild igual a TRUE. También puede tener otras ventanas (modales o no) como, por ejemplo, una ventana de configuración. Este tipo de proyecto podría ser, por ejemplo, una aplicación como Easy Code y es como está hecho el ejemplo MDI.

Una aplicación basada en un cuadro de diálogo ("dialog box") con una ventana principal que es un objeto DialogBox. Opcionalmente, puede tener otras ventanas (modales o no) como, por ejemplo, una ventana de configuración. De hecho, no hay prácticamente ninguna diferencia entre una aplicación que utilice un objeto Window o DialogBox como ventana principal.

Si desea generar una aplicación basada en un cuadro de diálogo, agregue un objeto DialogBox al proyecto (Proyecto-->Agregar Cuadro de Diálogo) y entonces quite la ventana Window1 creada por defecto una vez selecionada (Proyecto-->Quitar Window1). Finalmente, cambie la ventana de inicio, en las propiedades del proyecto, por la ventana DialogBox que acaba de agregar.

En cualquier momento puede cambiar los nombres por defecto de los objetos (por ejemplo wndMain para la ventana principal). Recuerde que el nombre del proyecto será el nombre del archivo ejecutable, por lo tanto, cámbielo por el nombre que desee que tenga su aplicación (vea Propiedades del proyecto).



APLICACIONES MDI

En realidad, un objeto MDIWindow está formado por dos ventanas, el MDI Frame y el MDI Client. Toda el área del cliente de una ventana MDI es el MDI Client, que contiene todas las ventanas MDI hijas. Como hemos visto antes, Easy Code tiene un método para obtener el "handle" del cliente MDI si es necesario. De todas maneras, todos los mensajes se reciben a través del procedimiento de ventana del MDI Frame y el "handle" de este MDI frame es el único válido para enviar comandos o para cualquier otra operación. Incluso cuando se crean ventanas MDI hijas en tiempo de ejecución, el parámetro que debe pasarse como ventana padre debe ser el "handle" del MDI frame, es decir, del objeto MDIWindow.

Si desea generar una aplicación MDI, agregue una ventana MDI al proyecto (Proyecto->Agregar ventana MDI). Easy Code cambiará la ventana principal (ventana de inicio) por el nombre de la ventana MDI agregada. Entonces, puede empezar a agregar o quitar otras ventanas dependiendo de sus necesidades. Recuerde que para que un objeto Window pueda ser una ventana MDI hija, su propiedad MDIChild debe ser igual a TRUE.

Los objetos MDIWindow no aceptan ningún otro control hijo que no sean los objetos Picture, ToolBar y StatusBar (estos objetos están dentro del MDI frame, no del MDI client), por lo tanto, puede agregar una ToolBar o una StatusBar, o también puede agregar un objeto Picture con otros controles en su interior, pero habitualmente, en este tipo de aplicaciones, las ventanas MDI hijas tienen todos los controles necesarios y hacen todo el trabajo. Cuando una ventana MDI hija se activa, su menú (si tiene alguno) se asigna a la ventana padre (la ventana MDI frame que es un objeto MDIWindow). Si la ventana MDI hija no tiene menú, la ventana MDI frame continua mostrando el suyo (si lo tiene). Aunque éste es el funcionamiento por defecto de las aplicaciones MDI, y el más apropiado, puede cambiar este comportamiento para cualquier ventana MDI hija simplemente interceptando su mensaje WM_MDIACTIVATE y devolviendo TRUE. Por ejemplo, supongamos que una ventana MDI hija se llama wndMDIChild, por lo tanto, su nombre de procedimiento DEBE ser wndMDIChildProcedure. Sólo es preciso que añada el código siguiente (el código añadido se muestra en letra negrita):

wndMDIChildProcedure Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_CREATE
Jne >
Call OnCreate
Ret
: Cmp D[uMsg], WM_MDIACTIVATE
Jne > L2
Return (TRUE)

L2: Return (FALSE)
EndF
OnCreate:
UseData wndMDIChildProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;
Return (FALSE)
EndU

Estas tres linias de código evitarán el comportamiento por defecto del menú de esa ventana MDI hija, pero su menú (si tiene uno) nunca aparecerá y, por ello, no podrá utilizarlo. El sistema operativo Windows permite que las ventanas hijas tengan menú, pero no pueden mostrarlo nunca por sí mísmas.

Por otra parte, y con el fin de evitar comportamientos extraños de las aplicaciones MDI, tenga en cuenta las siguientes consideraciones:

Cuando la ventana principal MDI frame coge el menú de la ventana MDI hija que está activa, es la ventana MDI frame (el objeto MDIWindow) quien recibe los comandos para ese menú, no la MDI hija a la que pertenece el menú. Estos comandos se envian a través del mensaje WM_COMMAND.

Para el mensaje WM_COMMAND de la ventana principal MDIWindow, ponga siempre FALSE como valor de retorno por defecto. Puede devolver TRUE cuando, por ejemplo, ha procesado un comando de menú, pero si devuelve siempre TRUE para el mensaje WM_COMMAND, se pueden producir comportamientos inesperados de las ventanas MDI hijas.

Cuando procese el mensaje WM_SIZE de una ventana MDI hija, devuelva SIEMPRE FALSE. Si devuelve TRUE, no podrá restaurar la ventana MDI hija después de haberla maximizado.

Procure recordar estas precauciones. A veces, al escribir código, es fácil cambiar el valor de retorno u olvidarse de alguna de estas precauciones. El resultado puede ser que su aplicación MDI no funcione correctamente o empiece a comportarse de un modo extraño.



AGREGANDO CONTROLES HIJO

Bién, ahora tiene una ventana vacía que se puede compilar sin errores, pero que no hace nada. Por lo tanto, empiece a diseñar su aplicación agregando controles hijo a las ventanas. Para agregar un control, haga clic en el botón correspondiente de la caja de herramientas (la forma del cursor encima del objeto ventana cambiará por una cruz). A continuación haga clic con el botón izquierdo del ratón sobre la ventana, mantenga el botón pulsado mientras lo arrastra hasta que el control tenga las dimensiones deseadas, y suelte el botón del ratón. Recuerde que los objetos Group y Picture también son contenedores, por consiguiente, también puede poner controles dentro de ellos.

También puede agregar un control haciendo doble clic sobre su botón correspondiente en la caja de herramientas. En ese caso, tenga presente que si el objeto activo es un Group o un Picture, el control pasarà a ser hijo suyo. En caso contrario, serà hijo del objeto ventana.

Easy Code también añade el correspondiente procedimiento para cada control que se agrega a una ventana (o a un objeto Group o Picture). En la mayoría de los casos, con los mensajes WM_COMMAND y WM_NOTIFY, recibidos por la "owner window" del control, será suficiente. Si es así, puede eliminar los procedimientos de aquellos controles que no necesite procesar (los procedimientos de los controles hijo no tienen que existir obligatoriamente). Cuando Easy Code no encuentra el nombre de procedimiento de un objeto control, simplemente lo ignora y entonces no se recibirán mensajes para ese objeto, excepto los que se envian a la "owner window" a través de los mensajes WM_COMMAND, WM_NOTIFY y WM_DRAWITEM. Por otra parte, cuando sí que desee procesar algún mensaje en concreto de un objeto control, su procedimiento debe existir y su nombre debe seguir la regla siguiente:

Los procedimientos de los objetos control (cualquiera de ellos) DEBEN tener un nombre que está formado por el nombre de la "owner window" mas su propio nombre de objeto.

Por ejemplo, un control Static llamado stcLabel que esté dentro de un objeto ventana llamado wndMain, DEBE tener el siguiente nombre de procedimiento:

wndMainstcLabel (respetando mayúsculas y minúsculas)

Además, los procedimientos de los objetos control también DEBEN tener los tener los clásicos cuatro parámetros:

hWnd, uMsg, wParam, lParam

Por lo tanto, el procedimiento de un control Static llamado stcLabel que esté dentro de un objeto ventana llamado wndMain será una cosa así:

wndMainstcLabel Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF

Esta regla para el nombre de procedimientos de controles es común a cualquier otro control que agregue, y es igual si el control está puesto directamente dentro de la "owner window" o dentro de cualquier otro control contenedor (Group o Picture).

Este es el código mínimo que se necesita para el procedimiento de cualquier objeto control. De todas maneras, no hace nada, por lo tanto, si no va a procesar ningún mensaje específico de ese control, puede eliminar todo el procedimiento.

Si el nombre del procedimiento de un objeto control no existe o no es correcto, Easy Code no da ningún error, pero NO RECIBIRÁ ningún otro mensaje para ese control que no sean los mensajes WM_COMMAND y WM_NOTIFY (o también WM_DRAWITEM si el control es owner draw) a través de su "owner window". Por otra parte, como se ha dicho para los objetos ventana, si procesa mensajes debe devolver TRUE para que no haya mas procesamiento, o FALSE para llamar al procedimiento por defecto (NUNCA cambie el valor de retorno por defecto a ningún otro valor que no sea FALSE). Para cualquier mensaje que deba procesar, añada el código correspondiente. Por ejemplo, si desea procesar el mensaje WM_SETFOCUS para un objeto control llamado stcLabel que está dentro de un objeto ventana llamado wndMain, escriba el código siguiente:

wndMainstcLabel Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_SETFOCUS
Jne >
;===========================================;
; El código del mensaje WM_SETFOCUS va aquí ;
;===========================================;

Return (TRUE)
: Return (FALSE)
EndF


OBSERVACIONES: Como se ha dicho anteriormente, Easy Code siempre escribe el correspondiente procedimiento para cada control que se agrega al proyecto. La mayoría de veces este procedimiento no se necesita para nada, puesto que hay suficiente con procesar los mensajes WM_COMMAND y WM_NOTIFY que se envian a la "owner window". Por consiguiente, si no tiene que procesar ningún mensaje en concreto para un control determinado, puede eliminar todo su procedimiento (eso ahorra algunos bytes en el archivo ejecutable final).



PERSONALIZANDO OBJETOS

Cuando agregue objetos ToolBar, StatusBar, TabStrip, ImageList, Header o Rebar podrá personalizarlos mediante la propiedad Personalizar, que se encuentra en la Ventana de propiedades y que muestra el cuadro de diálogo correspondiente. Para más información, vea los tópicos siguientes:

Personalizando objetos ToolBar
Personalizando objetos StatusBar
Personalizando objetos TabStrip
Personalizando objetos ImageList
Personalizando objetos Header
Personalizando objetos Rebar



OBTENIENDO LOS IDENTIFICADORES DE LOS CONTROLES

En tiempo de ejecución, todos los controles hijo que haya agregado en cada objeto ventana estarán creados y a punto para ser utilizados cuando se reciba el mensaje WM_CREATE de la ventana a la que pertenecen, es decir, la "owner window" (todavía no hay ningún control creado cuando se recibe el mensaje WM_NCCREATE). Cada control hijo tiene un identificador o ID, con su nombre de constante correspondiente (todo en mayúsculas). Para referirse a cualquier control utilizando ese nombre de constante, una vez mas debe tener en cuenta una simple regla. El nombre de constante de un control es siempre todo en mayúsculas y está formado por 'IDC_', mas el nombre de la "owner window", mas '_', mas el nombre del objeto control. Por ejemplo, el nombre de constante ID para un control Static llamado stcLabel, que pertenezca a una ventana llamada wndMain, será:

IDC_WNDMAIN_STCLABEL

La mayoria de las veces, la "owner window" y el padre del control son el mismo objeto (p.e. cuando se pone un control directamente en el objeto ventana), pero como hemos visto antes, hay dos tipos de controles (Group y Picture) que son contenedores y pueden tener controles hijo en su interior. Si es éste el caso, ello NO AFECTA al nombre de constante ID, que continua estando formado por IDC_, mas el nombre de la "owner window" (y no el de su padre, si es otro), mas '_', mas su propio nombre, lo cual hace su trabajo mas fácil no teniendo que recordar quién es el padre de quién.

Con estos nombres de constante, es realmente fácil referirse a cualquier control a lo largo del código. Esto se consigue utilizando el método de Easy Code GetWindowItem. Este método tiene dos parámetros, el "handle" de la "owner window" (o de cualquier otro objeto control que haya dentro) y el nombre de constante del control. Por ejemplo, dentro de un procedimiento de ventana (a la que pertenezca el control en cuestión):

wndMainProcedure Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_CREATE
Jne >
Call OnCreate
Ret
: Cmp D[uMsg], WM_CLOSE
Jne > L2
Call OnClose
Ret
L2: Return (FALSE)
EndF
OnCreate:
UseData wndMainProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;

Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' en el
; registro Eax. Hacer alguna operación

Return (FALSE)
EndU
OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU

Después de llamar al método GetWindowItem, el registro Eax contendrá el "handle" del control referido. Si es hijo de cualquier control contenedor (Group o Picture) o de mas de uno (p.e. un objeto control que es hijo de un Group, que es hijo de una Picture, que es hija de otra Picture, etc.), el primer argumento del método GetWindowItem puede ser directamente el "handle" de la "owner window" o el "handle" de cualquier otro control que haya dentro de la "owner window". Eso hace muy fácil referirse a cualquier control dentro del procedimiento de otro control (siempre, claro está, que pertenezcan a la misma "owner window"). Por ejemplo, supongamos que en la misma ventana (wndMain) tenemos otro control hijo que es un objeto Edit llamado edtEdit, y que dentro de su procedimiento (que DEBE tener el nombre wndMainedtEdit) queremos hacer alguna operación con el control Static llamado stcLabel:

wndMainedtEdit Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_LBUTTONDOWN
Jne > L2
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' en el
; registro Eax. Hacer alguna operación

Return (TRUE)
L2: Return (FALSE)
EndF

Puesto que cualquiera de los controles que hay dentro de la "owner window" es un "handle" válido para el primer argumento del método GetWindowItem, el "handle" del control edtEdit servirá. Cuando el control en cuestión pertenezca a otra ventana, puede utilizar el mismo método y de la misma manera, pero el primer argumento deberá ser el "handle" de la otra ventana o de cualquier control que haya dentro de la otra ventana (debe saber algún "handle"). Si esta otra ventana es la ventana principal, siempre puede obtener fácilmente su "handle" con App.Main. Por ejemplo, para obtener el "handle" de un control llamado stcLabel que pertenezca a la ventana principal desde el procedimiento de un control Edit llamado edtEdit (que pertenezca a la ventana principal o a cualquier otra ventana del proyecto):

wndMainedtEdit Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_LBUTTONDOWN
Jne > L2
Invoke GetWindowItem, App.Main , IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' en el
; registro Eax. Hacer alguna operación
Return (TRUE)
L2: Return (FALSE)
EndF


OBSERVACIONES: Debe ir con mucho cuidado cuando pase el primer parámetro al método GetWindowItem. Pasar un "handle" erróneo (p.e. un "handle" de otra ventana o de un control hijo de otra ventana) devolverá un "handle" incorrecto (el "handle" del control hijo de la otra ventana) o NULL.



PROBANDO VENTANAS

Después de agregar algunos controles y modificar sus propiedades, tal vez desee probar la ventana. Si es así, elija el menú Generar->Probar <NombreDeVentana> o pulse <Shift+F5>. La ventana que se probará será la que esté activa, y se mostrará de manera modal encima de Easy Code. Cuando se prueba una ventana no se procesa código alguno, pero puede visualizar como se verá en tiempo de ejecución, incluyendo los menús y recursos de cadena, o la navegación a través de sus controles con la tecla <Tab>, para comprobar el orden de tabulación (vea la propiedad TabOrder). Para cerrar una ventana que se está probando, pulse la tecla <Esc>.



Los mensajes WM_COMMAND, WM_NOTIFY y WM_DRAWITEM

Los objetos control envian mensajes a su padre. Como ya hemos visto antes, un control puede ser hijo de un objeto ventana, de otro control que sea contenedor (objetos Group o Picture), o incluso de un control que esté dentro de otros controles contenedores. En ese caso, ¿qué objeto recibe los mensajes WM_COMMAND, WM_NOTIFY y WM_DRAWITEM? Para hacer lo más fácil posible el escribir código de manera rápida (no teniendo que recordar quién es el padre de quién) esos mensajes los recibe SIEMPRE la "owner window". Por lo tanto, el código correspondiente deberá ponerse allí, en el mensaje WM_COMMAND, WM_NOTIFY o WM_DRAWITEM del procedimiento de ventana de la "owner window" a la que pertenezca el control.



CREANDO OBJETOS VENTANA EN TIEMPO DE EJECUCIÓN

Puede diseñar varios objetos ventana en su proyecto. Cuando la aplicación se inicia, y durante todo el tiempo que esté en marcha, puede crear dinámicamente esas ventanas en respuesta a las demandas del usuario. Todas las ventanas que se han agregado en tiempo de diseño están dentro del archivo ejecutable a punto para ser utilizadas cuando sea preciso. Para crearlas (vea el ejemplo MDI que viene con Easy Code), utilice el método Create. Este método devuelve el "handle" de la nueva ventana creada (si no es modal) o el valor de retorno (si es modal) en el registro Eax. Su sintaxis es la siguiente:

Invoke Create, lpszWindowName, hWndParentWindowHandle, lMode, lParam

El parámetro lpszWindowName es un valor DD con el puntero de la dirección efectiva de una cadena de texto acabada en cero, que contiene el nombre del objeto ventana (el que se indica en la propiedad Name, teniendo en cuenta las mayúsculas y minúsculas).

El parámetro hWndParentWindowHandle es un valor DD con el "handle" de la ventana padre. Este valor puede ser NULL (excepto para las ventanas MDI hijas) si la ventana que se quiere crear no ha de tener padre.

El parámetro lMode es un valor DD que indica si la ventana creada debe mostrarse de manera modal o no.

El parámetro lParam es el valor DD que se pasa a los cuadros de diálogo a través del parámetro lParam del mensaje WM_INITDIALOG. Este valor sólamente es para los objetos DialogBox, modales o no, y puede ser NULL si no se necesita.

Easy Code tiene dos constantes para el parámetro lMode, ecModal y ecModeless. De hecho, sus valores son, respectivamente, 1 (TRUE) y 0 (FALSE), por lo tanto, cualquier otro valor que no sea 0 significará TRUE, es decir, una ventana modal. Para evitar errores no deseados, utilice siempre las constantes ecModal y ecModeless.

Los objetos Window (siempre que tengan su propiedad MDIChild igual a FALSE) y DialogBox pueden ser modales o no, dependiendo del tercer parámetro del método Create, mientras que los objetos Window (que tengan su propiedad MDIChild igual a TRUE) y MDIWindow sólo pueden ser no modales y, por lo tanto, los parámetros tercero y cuarto son ignorados. De hecho, el tercer parámetro (lMode) sólo es válido para los objetos Window y DialogBox, mientras que el cuarto parámetro (lParam) sólo es válido para los objetos DialogBox.

Para las aplicaciones MDI, Easy Code tiene el método GetMDIClient para obtener el "handle" del cliente MDI. De todas maneras, cuando se crean ventanas MDI hijas, el "handle" que debe pasarse al método Create como ventana padre (segundo parámetro) es el de la ventana MDI frame (el objeto MDIWindow), y no el que devuelve el método GetMDIClient.

Bién, ahora supongamos que tenemos un proyecto con los objetos siguientes (los nombres de los objetos se muestran en color azul):

Un objeto principal Window llamado wndMain y un objeto DialogBox llamado dlgOptions. Dentro de la ventana principal (wndMain), tenemos un objeto Button con el nombre de btnShow. Queremos que la DialogBox (dlgOptions) se muestre de manera modal cuando hagamos clic en el objeto Button (btnShow), y que la ventana principal (wndMain) sea el padre. En el objeto DialogBox (dlgOptions), tenemos dos objetos Button llamados btnOK y btnCancel, y ambos cierran el cuadro de diálogo modal (btnOK lo cierra validando y btnCancel lo cierra cancelando).

El nombre del procedimiento de la ventana principal debe ser wndMainProcedure.
El nombre del objeto DialogBox que debe crearse es dlgOptions.
El ID del control Button (btnShow) debe ser IDC_WNDMAIN_BTNSHOW (porque pertenece al objeto wndMain y su nombre es btnShow).

El nombre del procedimiento del cuadro de diálogo debe ser dlgOptionsProcedure.
El ID del control Button (btnOK) debe ser IDC_DLGOPTIONS_BTNOK (porque pertenece al objeto dlgOptions y su nombre es btnOK).
El ID del control Button (btnCancel) debe ser IDC_DLGOPTIONS_BTNCANCEL (porque pertenece al objeto dlgOptions y su nombre es btnCancel).

La "owner window" es el objeto que recibe los mensajes WM_COMMAND enviados por cualquiera de sus controles hijo (en este ejemplo los mensajes de los clics del botón btnShow). Por ello, dentro del mensaje WM_COMMAND (rutina OnCommand) del procedimento de la ventana principal (wndMainProcedure), escribiremos el código siguiente:

wndMainProcedure Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_CREATE
Jne >
Call OnCreate
Ret
: Cmp D[uMsg], WM_COMMAND
Jne >
Call OnCommand
Ret
: Cmp D[uMsg], WM_CLOSE
Jne > L2
Call OnClose
Ret
L2: Return (FALSE)
EndF
OnCreate:
UseData wndMainProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;
Return (FALSE)
EndU
OnCommand:
UseData wndMainProcedure
LoWord ([wParam])
Cmp Ax, IDC_WNDMAIN_BTNSHOW
Jne > L2
HiWord ([wParam])
Cmp Ax, BN_CLICKED
Jne > L2
Invoke Create, TEXT("dlgOptions"), hWnd, ecModal, NULL
Return (TRUE)
L2: Return (FALSE)
EndU
OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU


mientras que dentro del mensaje WM_COMMAND (rutina OnCommand) del cuadro de diálogo (dlgOptionsProcedure) escribiremos el código siguiente:

dlgOptionsProcedure Frame hDlg, uMsg, wParam, lParam
Cmp D[uMsg], WM_INITDIALOG
Jne >
;======================================;
; Aquí lParam tiene el valor pasado al ;
; método Create como cuarto parámetro ;
;======================================;

Call OnInitDialog
Ret
: Cmp D[uMsg], WM_COMMAND
Jne >
Call OnCommand
Ret
: Cmp D[uMsg], WM_CLOSE
Jne > L2
Call OnClose
Ret
L2: Return (FALSE)
EndF
OnInitDialog:
UseData dlgOptionsProcedure
;=====================================;
; El código de inicialización va aquí ;
;=====================================;
Return (FALSE)
EndU
OnCommand:
UseData dlgOptionsProcedure
LoWord ([wParam])
Cmp Ax, IDC_DLGOPTIONS_BTNOK
Jne >
HiWord ([wParam])
Cmp Ax, BN_CLICKED
Jne > L2
Invoke EndModal, hDlg, IDOK
Return (TRUE)
: Cmp Ax, IDC_DLGOPTIONS_BTNCANCEL
Jne > L2
HiWord ([wParam])
Cmp Ax, BN_CLICKED
Jne > L2
Invoke EndModal, hDlg, IDCANCEL
Return (TRUE)
L2: Return (FALSE)
EndU
OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;

Invoke IsModal, hDlg
Or Eax, Eax
Jz >
Invoke EndModal, hDlg, IDCANCEL
Mov Eax, TRUE
: Ret
EndU

LoWord y HiWord son macros de Easy Code que devuelven, respectivamente, la palabra baja ("low word") y la palabra alta ("high word"), valores de 16 bits (o DW) de un valor de 32 bits (o DD) que se pasa como argumento (en este ejemplo wParam). El valor de 16 bits se devuelve en el registro Eax (de hecho, se encuentra en el registro Ax). TEXT es otra macro de Easy Code que crea una cadena de texto local y temporal (en este ejemplo con el texto "dlgOptions", que es el nombre del objeto DialogBox). No hace falta ningún carácter NULL al final de la cadena porque la macro TEXT lo añade internamente. Finalmente, en el mensaje WM_COMMAND de la "owner window" que recibe los clics de los botones btnOK y btnCancel (el objeto DialogBox), llamamos a otro método de Easy Code, EndModal, que cierra (destruye) una ventana modal y devuelve el valor indicado (en este ejemplo IDOK o IDCANCEL). El método EndModal tiene dos parámetros, el "handle" de la ventana que debe destruirse y el valor de retorno. Este método devuelve TRUE o FALSE, en el registro Eax, dependiendo de si la ventana se ha destruído o no (p.e. si llama a este método para un objeto ventana que no es modal, no hará nada y devolverá FALSE), y se puede llamar desde cualquier parte del código. Una vez se ha llamado, la ventana modal ha sido definitivamente destruída. Es por ello que debe devolverse TRUE después de llamar al método EndModal, para que no haya mas procesamiento para el mensaje WM_CLOSE.

Otra manera de cerrar una ventana modal con IDCANCEL como valor de retorno, es enviar un mensaje WM_CLOSE. En el ejemplo que hemos visto, el código del objeto Button para cancelar (btnCancel) dentro del mensaje WM_COMMAND (rutina OnCommand) del procedimiento del cuadro de diálogo (dlgOptions), se podría modificar por el código siguiente (el texto modificado se muestra en letra negrita):

OnCommand:
UseData dlgOptionsProcedure
LoWord ([wParam])
Cmp Ax, IDC_DLGOPTIONS_BTNOK
Jne >
HiWord ([wParam])
Cmp Ax, BN_CLICKED
Jne > L2
Invoke EndModal, hDlg, IDOK
Return (TRUE)
: Cmp Ax, IDC_DLGOPTIONS_BTNCANCEL
Jne > L2
HiWord ([wParam])
Cmp Ax, BN_CLICKED
Jne > L2
Invoke SendMessage, hDlg, WM_CLOSE, 0, 0
Return (TRUE)
L2: Return (FALSE)
EndU

Note que después de enviar el mensaje WM_CLOSE debe devolver TRUE con el fin de que no haya mas procesamiento para el mensaje WM_COMMAND, puesto que la ventana ya ha sido destruída (no existe) y continuar procesando el mensaje podría producir graves errores. Por otra parte, cuando procese el mensaje WM_CLOSE para una ventana modal, y sólamente si la ventana es modal, puede quitar algunas lineas de código:

	Cmp D[uMsg], WM_CLOSE
Jne > L2
Invoke EndModal, hDlg, IDCANCEL
Return (TRUE)
L2: Return (FALSE)

Y eso es todo lo que necesita para crear un objeto ventana en tiempo de ejecución, que haya sido diseñada en tiempo de diseño. Fácil, ¿verdad?

IMPORTANTE: El método EndModal sólo funciona con ventanas modales. Si lo invoca con el "handle" de una ventana no modal, el método devolverá FALSE y la ventana no será destruída. Por otra parte, después de llamar al método EndModal con un "handle" válido (el de una ventana modal), la ventana ya estará destruída, por lo tanto, debe devolver TRUE para que no haya mas procesamiento, puesto que el objeto ya no existe. De otro modo, pueden producirse errores.



El mensaje WM_CREATE

Cuando una ventana recibe este mensaje, todos los controles hijo que se hayan agregado en tiempo de diseño ya están creados y disponibles (utilice su ID constante para cualquier operación). Como se ha dicho antes, este mensaje debe devolver cualquier valor diferente de -1 para continuar la creación de la ventana (un valor de retorno FALSE o TRUE será correcto). Si devuelve TRUE, el foco se asigna al primer control que pueda tenerlo (vea la propiedad TabOrder). La mayoría de las veces, este es el valor de retorno mas habitual. Contrariamente, si asigna el foco a cualquier otro control durante el procesamiento de este mensaje (función de la API SetFocus), deberá devolver FALSE para que el foco continue asignado al control especificado. Finalmente, recuerde que un valor de retorno de -1 destruirá la ventana antes de que sea visible.

OBSERVACIONES: Los Cuadros de diálogo no reciben el mensaje WM_CREATE. En su lugar, reciben el mensaje WM_INITDIALOG con el parámetro lParam puesto al valor que se haya pasado al método Create como cuarto parámetro. Este parámetro puede ser NULL si no se necesita.



El mensaje ECM_AFTERCREATE

Justo después de crear una ventana, y antes de que sea visible, Easy Code envia el mensaje ECM_AFTERCREATE al procedimiento de la ventana. Este mensaje puede ser útil para hacer algún tipo de inicialización o cambiar aquello que no se haya podido llevar a cabo durante el mensaje WM_CREATE. En este punto, la ventana y todos sus hijos están completamente creados y a punto de ser mostrados. El mensaje ECM_AFTERCREATE es exclusivo de Easy Code, su valor es WM_USER + 1049 y su sintaxis es corregida automáticamente a mayúsculas por el IDE.

OBSERVACIONES: El valor de retorno de este mensaje indica si la ventana debe destruirse o no. Si este valor es -1, la ventana será destruïda antes de ser mostrada, mientras que cualquier otro valor permitirá que el proceso continue.



El mensaje WM_CLOSE

Un objeto Window (que no sea una ventana MDI hija) y un objeto DialogBox pueden ser modales o no. Cuando se crea una ventana modal, su padre (si tiene) es deshabilitado y no tiene control alguno hasta que se cierra la ventana modal. En este punto, se espera un valor de retorno que nos diga si el usuario ha validado o cancelado las opciones que se le ofrecían en la ventana modal. Este valor (habitualmente IDOK o IDCANCEL) se encuentra en el registro Eax cuando el método Create regresa, y es el valor que se ha pasado al método EndModal cuando se ha destruído la ventana modal.

Por otra parte, cuando se crean ventanas no modales, el método Create regresa inmediatamente con el "handle" de la nueva ventana creada en el registro Eax. Si la ventana no se ha podido crear por cualquier motivo (por ejemplo si el nombre no existe o no es correcto, porque recuerde que los nombres de objeto diferencian las mayúsculas de las minúsculas), entonces el valor de retorno será NULL.

En ambos casos, puede evitar que la ventana sea destruída devolviendo TRUE en el mensaje WM_CLOSE (p.e. si pide confirmación), mientras que devolviendo FALSE la ventana se cerrará (destruirá) normalmente. En las ventanas modales, cualquier confirmación debe pedirse antes de llamar al método EndModal, puesto que después de llamarlo la ventana ya está destruída. Por lo tanto, todo el código para evitar que se cierre y destruya un objeto ventana debe ponerse en el mensaje WM_CLOSE. Si, por ejemplo, ha pedido confirmación para cerrar y el usuario ha respondido que no, simplemente devuelva TRUE. En las ventanas modales, devuelva también TRUE pero sin llamar al método EndModal.

Cuando agregue objetos Window y DialogBox al proyecto (puesto que ambos pueden ser modales o no), Easy code escribe el código necesario en el mensaje WM_CLOSE para que la ventana se cierre propiamente, tanto si es modal como si no. Este código (en letra negrita) es el siguiente:

OnClose:
UseData wndMainProcedure
;=========================;
; El código final va aquí ;
;=========================;

Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE

: Ret
EndU

Cuando se cierra la ventana, se llama a otro método de Easy Code, IsModal, que devuelve TRUE o FALSE en el registro Eax, dependiendo de si la ventana es modal o no. Si es modal, debe llamarse al método EndModal para cerrar correctamente, mientras que si no es modal, la ventana se cierra normalmente devolviendo FALSE.

OBSERVACIONES: El mensaje WM_CLOSE es la última oportunidad de evitar que la ventana sea destruída. Después de eso, recibirá los mensajes WM_DESTROY y WM_NCDESTROY, pero sólo para hacer limpieza (liberar recursos utilizados, etc.). En el momento de estos dos últimos mensajes, ya no hay nada que hacer. ¡La ventana se está destruyendo!

IMPORTANTE: ¡Vaya con cuidado! Devolver siempre TRUE para un mensaje WM_CLOSE, significa que la ventana no se destruye nunca. Si es una ventana modal, y no llama al método EndModal, su aplicación no podrá cerrarse nunca de manera correcta.



CUADROS DE DIÁLOGO

Los cuadros de diálogo ("dialog boxes") se utilizan fecuentemente en las aplicaciones Windows para intercambiar información con el usuario. Pueden ser modales o no y se crean a partir de una plantilla, habitualmente del archivo de recursos del programa. Para calcular las dimensiones de un cuadro de diálogo, Windows utiliza el promedio de la altura y la anchura de su fuente. Todos los controles que hay dentro de un cuadro de diálogo utilizan la misma fuente que su padre (el cuadro de diálogo), por lo tanto, no pueden tener su propia fuente como en cualquier otra ventana. Utilizar sólamente una fuente para todos los objetos, permite que el cuadro de diálogo y sus hijos se muestren siempre proporcionalmente en diferentes resoluciones de pantalla y tamaños de fuente. Cuando se cambia la propiedad Font de un cuadro de diálogo en tiempo de diseño, él y todos sus controles hijo son redimensionados de acuerdo con el tamaño de la nueva fuente, mientras que si se cambia la fuente en tiempo de ejecución, sólamente cambia la fuente, pero no se lleva a cabo ninguna redimensión.

Los proyectos visuales tienen su propio cuadro de diálogo, el objeto DialogBox, que ofrece algunas prestaciones mas que los cuadros de diálogo de los proyectos clásicos. Todos los objetos DialogBox que necesita una aplicación, diseñados en el entorno visual (tiempo de diseño), pueden ser creados en tiempo de ejecución llamando al método Create. Una vez creado, un objeto DialogBox (modal o no) no recibe nunca el mensaje WM_CREATE, en su lugar recibe el mensaje WM_INITDIALOG, antes que la ventana sea visible, donde se puede escribir todo el código necesario. Cuando se recibe este mensaje, el parámetro lParam tiene el valor que se haya pasado como cuarto parámetro al método Create. Este parámetro puede ser cualquier valor que sea preciso pasar al cuadro de diálogo, por ejemplo la dirección de una estructura de datos, o puede ser NULL si no se necesita.

El valor de retorno de un mensaje WM_INITDIALOG tiene un significado especial (también aplicado al mensaje WM_CREATE de los objetos Window). Es el siguiente:

Si devuelve TRUE, el foco se asigna al primer control que pueda tenerlo (vea la propiedad TabOrder). Mayoritariamente, este es el valor de retorno habitual.

Si asigna el foco a cualquier otro control durante la ejecución de este mensaje, llamando a la función de la API SetFocus, debería devolver FALSE para que el foco continue asignado al control especificado.

Cuando destruya una ventana modal (sea un objeto DialogBox o Window), debe llamar al método de Easy Code EndModal. Esta es la manera correcta de destruir una ventana modal. No llame NUNCA a la función de la API DestroyWindow si la ventana que debe destruirse es modal.



PROCESANDO EL TIEMPO "IDLE"

Cuando la aplicación no tiene ningún mensaje para procesar, se dice que está idle y no hace otra cosa que esperar a que le lleguen mas mensajes. Puede aprovechar este tiempo idle para llevar a cabo algún tipo de procesamiento en segundo plano. Para ello, sólo tiene que escribir el siguiente procedimiento:

OnIdle Frame lCount
Return (FALSE)
EndF

El nombre de este procedimiento DEBE ser OnIdle (respetando mayúsculas y minúsculas), debe estar en la ventana de inicio y se llama cuando no hay ningún mensaje que procesar. El parámetro lCount se incrementa cada vez que se llama a OnIdle dentro del mismo ciclo y se pone a 0 cada vez que se procesa un nuevo mensaje, por lo tanto, puede saber cuando empieza un nuevo ciclo idle comprobando que el valor del parámetro lCount sea 0. Basándose en esta cuenta, puede llamar a diferentes rutinas idle:

OnIdle Frame lCount
Cmp D[lCount], 0
Jne >
; Hacer alguna tarea
Return (TRUE) ; o FALSE
: Cmp D[lCount], 1
Jne >
; Hacer alguna otra tarea
Return (TRUE) ; o FALSE
: Cmp D[lCount], 50
Jge >
Return (TRUE)
: Return (FALSE)
EndF

Este procedimiento debe devolver TRUE para continuar recibiendo mas tiempo de procesamiento "idle" dentro del mismo ciclo. Contrariamente, si devuelve FALSE, el procedimiento ya no se llamará mas durante el ciclo actual, es decir, no se volverá a llamar hasta el próximo ciclo "idle".

Es muy importante tener en cuenta el comportamiento del procedimiento OnIdle para no sobrecargar el procesador. Si este procedimiento devuelve siempre TRUE, el procesador estará permanentemente sobrecargado, por lo tanto, una buena práctica es llevar a cabo diferentes pequeñas tareas hasta que lCount alcance un valor determinado (50 en el ejemplo anterior).


OBSERVACIONES: El procedimiento OnIdle se utiliza para llevar a cabo tareas simples en segundo plano. Las tareas mas largas deberían dividirse en muchas pequeñas rutinas que se llamarían secuencialmente en función del parámetro lCount.



ACCEDIENDO A LA FUNCIÓN WinMain DE WINDOWS (CUANDO LA APLICACIÓN SE INICIA Y/O FINALIZA)

También puede obtener el control cuando la aplicación se inicia y/o finaliza (directamente desde el inicio y el final de la función WinMain), simplemente agregando, respectivamente, los procedimientos MainStart y/o MainEnd (respetando mayúsculas y minúsculas) en la sección .Code de la ventana principal (ventana de inicio) de la aplicación:

MainStart Frame hInstance, hPrevInst, lpCmdLine, nCmdShow
	; Escriba su código aquí
Ret
EndF

MainEnd Frame hInstance, hPrevInst, lpCmdLine, nCmdShow ; Escriba su código aquí
Ret
EndF

Cuando la función WinMain se inicia, llama al procedimiento MainStart (si existe) y cuando la función WinMain finaliza, llama al procedimiento MainEnd (si existe). Debe tener mucho cuidado con el código que escribe en estos dos procedimientos (especialmente el primero), puesto que puede bloquear la aplicación. En ambos procedimientos, el valor de retorno es indiferente.




CONTROLANDO EL BUCLE DE MENSAJES DE LA APLICACIÓN

En muchos casos es suficiente procesar los mensajes dentro de los procedimientos de los objetos. Aun así, si necesita mas control y desea comprobar los mensajes antes de que sean procesados, escriba el código siguiente en la sección .Code de la ventana principal (ventana de inicio) de la aplicación.

ProcessMessages Frame hWnd, uMsg, wParam, lParam
Return (FALSE)
EndF

El nombre de este procedimiento DEBE ser ProcessMessages (respetando mayúsculas y minúsculas) y se llama directamente desde el bucle de mensajes de la aplicación, antes que a ninguna otra función de la API. Si ProcessMessages existe, es decir, si su procedimiento está presente, DEBE tener el nombre indicado y estar en la ventana principal (ventana de inicio) de la aplicación, para que Easy Code pueda llamarlo a cada mensaje.

ProcessMessages le permite comprobar el objeto de destino (hWnd), el mensaje (uMsg) y los dos parámetros del mensaje (wParam y lParam). Si devuelve TRUE, el mensaje nunca será procesado. Como puede ver, este procedimiento le proporciona un control total sobre la aplicación. Supongamos que no desea que se procese el mensaje WM_KEYDOWN para un objeto Static llamado stcLabel, que pertenece a la ventana principal de la aplicación (llamada wndMain). El nombre de constante ID para el objeto stcLabel será IDC_WNDMAIN_STCLABEL (porque está dentro de wndMain y su nombre es stcLabel).

Sólo tiene que escribir este código:

ProcessMessages Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_KEYDOWN
Jne > L2
Invoke GetWindowItem, App.Main, IDC_WNDMAIN_STCLABEL
; "Handle" de 'stcLabel' en el registro Eax
Cmp Eax, hWnd
Jne >L2
Return (TRUE)
L2: Return (FALSE)
EndF

En este ejemplo, podemos saber fácilmente el "handle" de stcLabel porque está dentro de la ventana principal (App.Main siempre contiene el "handle" de la ventana principal). De no ser así, deberíamos saber cual es la "owner window" del control que queremos comprobar.

En la mayoría de aplicaciones este procedimiento no se necesita para nada. Puesto que usted decide cual es la ventana principal (donde debe estar este procedimiento), y si utilizará ProcessMessages o no, Easy Code no lo añade nunca por defecto.

IMPORTANTE: Vaya con mucho cuidado con este procedimiento y no cambie nunca el valor de retorno por defecto FALSE. Puesto que devolver TRUE significa que el mensaje no se procesa (se ignora), si siempre devuelve TRUE (es decir, cualquier otro valor que no sea FALSE), querrá decir que no se procesará ningún mensaje y su aplicación se bloqueará.



PRESERVANDO LOS REGISTROS EN LOS PROYECTOS VISUALES

Cuando trabaje con proyectos visuales, puede utilizar libremente los registros Ebx, Ecx, Edx, Edi, y Esi en todos los procedimientos del proyecto sin guardarlos (excepto para su conveniencia cuando necesite guardar alguno), porque Easy Code ya se cuida de ello. Por otra parte, cuando llame a cualquier método de Easy Code, puede confiar en que ninguno de estos registros será modificado.



OBJETOS - PROPIEDADES Y MÉTODOS

Cada objeto, ventana o control, tiene unas propiedades que modifican su apariencia y funcionamiento. Modificando estas propiedades y moviendo y redimensionando los controles, puede diseñar fácilmente una aplicación en el entorno de desarrollo Easy Code. A lo largo de todo el código, dentro de los procedimientos de cada objeto, puede modificar propiedades y llamar métodos. El código que escriba allí será llevado a cabo en tiempo de ejecución (cuando la aplicación esté en marcha). Para ello, Easy Code dispone de varios métodos muy útiles para intercambiar información con los objetos y llevar a cabo operaciones diversas. Es preciso que se familiarice con todas esas propiedades y métodos, y programar una gran aplicación Windows de 32 bits en ensamblador será fácil, rápido y posible.

Propiedades de los objetos
Leyendo y escribiendo propiedades
Métodos de Easy Code

Para ilustrar todo lo que aquí se ha dicho, vea los ejemplos que vienen con la aplicación Easy Code (ubicados en la carpeta Examples del directorio de Easy Code). Las aplicaciones File Shredder, MIDI Player, MDI, RGG y TabStrip (proyectos visuales) han sido programadas totalmente con Easy Code y utilizan todo el estilo de programación que se ha visto en este capítulo.