Projectes visuals - El mode potent de l'Easy Code






Quan es tria l'opció Fitxer executable visual, es crea un projecte visual per defecte anomenat Project1. Aquest tipus de projecte li diu automàticament a l'Easy Code que assocïi la seva llibreria visual, que és necessària per a totes les funcionalitats visuals.

Hi ha dues maneres d'enllaçar la llibreria visual, estàtica o dinàmica, i cal indicar-ne una d'elles a les propietats del projecte, disponibles a través del menú "Projecte-->Propietats". El mode dinàmic generarà l'aplicació amb la llibreria visual necessària en un fitxer a banda (ECDllGo.dll o ECDllGou.dll) que s'ha de distribuir amb l'aplicació, mentre que el mode estàtic inclourà la llibreria visual necessària (ECStcGo.lib o ECStcGou.ib) dins del fitxer executable.

El quadre de verificació Manifest.xml indica si s'activaran els nous "comon controls" que es troben disponibles des del Windows XP i posteriors. Per a més informació, consulteu el tòpic Incloent un Manifest al projecte.


RECURSOS VISUALS DE L'EASY CODE

El mode visual de l'Easy Code divideix els objectes en dos grups: Finestres i Controls.

Finestres

Les finestres són contenidors, és a dir, poden tenir controls fill dintre seu. L'Easy Code maneja tres tipus d'objectes finestra:

Window: Finestres corrents utilitzades en petites aplicacions com a finestres principals. Un objecte Window pot ser modal (si no és la finestra principal), una finestra MDI filla, o una finestra no modal.

DialogBox: Utilitzades principalment com a finestres modals per tal de seleccionar o modificar opcions de la finestra principal de l'aplicació que les crea. Un objecte DialogBox també pot ser no modal i utilitzar-se com a finestra principal d'una aplicació.

MDIWindow: Utilitzades com a finestres principals a les aplicacions MDI. Aquestes finestres poden tenir altres finestres del tipus MDI filla dintre seu. Un projecte visual de l'Easy Code només pot tenir un objecte MDIWindow.

Controls

Els controls són petites finestres situades dins dels objectes Window i DialogBox. També poden posar-se dintre dels dos tipus de controls contenidors, Group i Picture. L'Easy Code maneja diversos objectes control (per una llista de controls disponibles vegeu Objectes control).


NOTA
: No cal que inicialitzeu els "Common Controls", funció de l'API "InitCommonControlsEx", atès que l'Easy Code els inicialitza internament depenent de l'opció Common controls de les propietats del projecte.



FINESTRA PROPIETÀRIA ("OWNER WINDOW")

La finestra de més alt nivell d'un objecte control, fins i tot si és fill d'un altre control que sigui contenidor (com Group o Picture), és l'objecte Window, MDIWindow o DialogBox que els conté a tots ells, i que rep el nom de "Owner window". Quan una "owner window" rep el missatge WM_CREATE, tots els controls fill estan creats i disponibles. Si la "owner window" és un objecte MDIWindow, la seva finestra MDIClient també està creada, i el seu "handle" es pot obtenir cridant el mètode de l'Easy Code GetMDIClient (vegeu Aplicacions MDI).

NOTA: En tot moment podeu saber quina és la "owner window" d'un objecte control cridant el mètode de l'Easy Code the GetOwnerWindow.

Les llibreries visuals de l'Easy Code, ECStcGo.lib o ECStcGou.lib per enllaç estàtic i ECDllGo.dll o ECDllGou.dll per enllaç dinàmic, estan fetes en llenguatge assemblador i controlen el funcionament de tots els objectes, ocupant-se de la navegació entre els controls a través de la tecla <Tab>, tecles ràpides dels menús, aparença, text i colors dels objectes, etc.. A més d'aquestes llibreries, el mode visual de l'Easy Code sempre inclou els següents fitxers:

Fitxers Include:

windows.inc

Fitxers d'enllaç:

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


Per tant, no necessiteu incloure cap d'aquests fitxers al vostre projecte, perquè si ho feu seran ignorats. La majoria de projectes només necessiten aquests fitxers, o fins i tot algun menys.



PROCEDIMENTS DE FINESTRA

Tots els objectes d'un projecte, finestres i controls, tenen propietats (vegeu Propietats dels objectes) que poden modificar la seva aparença i funcionament. Una de les propietats més importants és Name, atès que identifica l'objecte dins la seva "owner window" (Window, MDIWindow o DialogBox) i forma part del nom del seu procediment de finestra. L'Easy Code escriu la corresponent plantilla de codi per a les finestres i controls conforme es van afegint al projecte. Dins d'aquests procediments podeu interceptar els missatges que us interessin i escriure el codi addient. A l'hora de compilar, l'Easy Code comprova que hi hagi el corresponent procediment de finestra per a cadascun dels objectes Window, MDWindow i DialogBox del projecte. Si algun d'ells no hi és, o el seu nom de procediment és incorrecte, es genera un error. Tots els procediments d'objectes finestra HAN d'existir a l'hora de compilar.

Com sap l'Easy Code el nom correcte de cada procediment de finestra? Per saber-ho es basa en una simple regla que utilitza el nom de l'objecte més la paraula "Procedure". És a dir, els procediments dels objectes Window, MDIWindow o DialogBox, HAN de tenir un nom que es forma amb el propi nom de l'objecte (indicat per la seva propietat Name) més la paraula "Procedure". Per exemple, un objecte finestra anomenat wndMain ha de tenir el nom de procediment següent:

wndMainProcedure (respectant majúscules i minúscules)

D'altra banda, els procediments d'objectes Window, MDIWindow o DialogBox també HAN de tenir els clàssics quatre paràmetres:

hWnd, uMsg, wParam, lParam


Per tant, el procediment (o Frame) d'un objecte finestra anomenat wndMain serà més o menys així:

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 codi d'inicialització va aquí ;
;==================================;
Return (FALSE)
EndU
OnClose:
UseData wndMainProcedure
;=======================;
; El codi final va aquí ;
;=======================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU


Aquest és el codi mínim que necessita el procediment d'un objecte finestra, però no fa res més que crear l'objecte i destruir-lo (analitzarem el missatge WM_CLOSE més endavant). Return és una macro de l'Easy Code (vegeu Macros de l'Easy Code), que fa que el valor de retorn sigui més ràpid i més clar.

Per cada missatge que vulgueu processar, afegiu el codi corresponent. Per exemple, per processar el missatge WM_SIZE afegiu el codi següent:

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 codi d'inicialització va aquí ;
;==================================;
Return (FALSE)
EndU
OnSize:
UseData wndMainProcedure
;======================================;
; El codi del missatge WM_SIZE va aquí ;
;======================================;
Return (TRUE)
EndU
OnClose:
UseData wndMainProcedure
;=======================;
; El codi final va aquí ;
;=======================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU

Com podeu veure, el procediment de finestra sempre torna FALSE o TRUE per tal de funcionar correctament. Quan es torna TRUE (qualsevol valor que no sigui 0), no hi haurà cap més processament per aquest missatge. Contràriament, si el valor de retorn és FALSE, es cridarà el procediment de finestra per defecte del Windows. Aquesta és la raó per la que el valor de retorn per defecte, al final del procediment, és FALSE. Tots els missatges que no processeu seran processats pel procediment de finestra per defecte, que és part de l'API del Windows, per tal d'evitar resultats no desitjats. Per tant, no canvieu mai el valor de retorn per defecte FALSE i simplement torneu TRUE per a tots aquells missatges que no vulgueu que continuïn sent processats. Aquesta regla, però, té les següents excepcions:

El missatge WM_NCCREATE ignora el valor de retorn i continua la creació de la finestra.

El missatge WM_CREATE ha de tornar qualsevol valor diferent de -1 per tal de continuar la creació de la finestra. És recomanable que aquest valor de retorn sigui TRUE o FALSE. Si és TRUE, el focus s'assignarà al primer control que pugui tenir-lo (vegeu la propietat TabOrder). La majoria de vegades, aquest és el valor de retorn més utilitzat per tal que el focus s'assigni al primer control disponible. Altrament, si assigneu el focus a qualsevol altre control mentre processeu aquest missatge (rutina de l'API SetFocus), heu de tornar FALSE per tal que el focus quedi assignat al control en qüestió. Finalment, un valor de retorn de -1 destruïrà la finestra abans de que sigui visible.

Els missatges WM_DESTROY i WM_NCDESTROY s'envien abans de destruir l'objecte per tal de poder alliberar els recursos utilitzats ("clean up"), però ignoren el valor de retorn i destrueixen la finestra igualment.



CREANT UN NOU PROJECTE VISUAL

Quan es crea un nou projecte visual, s'afegeix automàticament una finestra amb el nom de "Window1". Si no la canvieu, aquesta serà la finestra principal o d'inici ("main window"). Qualsevol de les finestres del projecte pot ser la finestra principal o d'inici, és a dir, la finestra que apareix en primer lloc i que és la finestra principal de l'aplicació (l'objecte App sempre torna el "handle" de la finestra principal a través del seu membre Main). En aquest punt, podeu dissenyar tres tipus d'aplicacions:

Una aplicació bàsica amb una finestra principal que és un objecte Window. Opcionalment, pot tenir altres finestres (modals o no) com, per exemple, una finestra de configuració. Aquest és el projecte creat per defecte i és com estan fetes les aplicacions d'exemple File Shredder, MIDI Player, RGG i TabStrip.

Una aplicació MDI amb una finestra principal MDI, un objecte MDIWindow, i tantes finestres MDI filla com calguin. Les finestres MDI filla són finestres normals (objectes Window), però amb la seva propietat MDIChild igual a TRUE. També pot tenir altres finestres (modals o no) com, per exemple, una finestra de configuració. Aquest tipus de projecte podria ser, per exemple, una aplicació com l'Easy Code i és com està feta l'exemple MDI.

Una aplicació basada en un quadre de diàleg ("dialog box") amb una finestra principal que és un objecte DialogBox. Opcionalment, pot tenir altres finestres (modals o no) com, per exemple, una finestra de configuració. De fet, no hi ha pràcticament cap diferència entre una aplicació que utilitzi un objecte Window o DialogBox com a finestra principal.

Si voleu generar una aplicació basada en un quadre de diàleg, afegiu un objecte DialogBox al projecte (Projecte-->Afegeix un Quadre de Diàleg) i llavors traieu la finestra Window1 creada per defecte un cop selecionada (Projecte-->Treu Window1). Finalment, canvieu la finestra d'inici, a les propietats del projecte, per la finestra DialogBox que acabeu d'afegir.

En qualsevol moment podeu canviar els noms per defecte dels objectes (per exemple wndMain per la finestra principal). Recordeu que el nom del projecte serà el nom del fitxer executable, per tant, canvieu-lo pel nom que vulgueu que tingui la vostra aplicació (vegeu Propietats del projecte).



APLICACIONS MDI

En realitat, un objecte MDIWindow està format per dues finestres, l'MDI Frame i l'MDI Client. Tota l'àrea del client d'una finestra MDI és l'MDI Client, que conté totes les finestres MDI filles. Com hem vist abans, l'Easy Code té un mètode per obtenir el "handle" del client MDI si és necessari. De tota manera, tots els missatges es reben a través del procediment de finestra de l'MDI Frame i el "handle" d'aquest MDI frame és l'únic vàlid per enviar comandes o per a qualsevol altra operació. Fins i tot quan es creen finestres MDI filles en temps d'execució, el paràmetre que s'ha de passar com a finestra pare és el "handle" de l'MDI frame, és a dir, de l'objecte MDIWindow.

Si voleu generar una aplicació MDI, afegiu una finestra MDI al projecte (Projecte->Afegeix una finestra MDI). L'Easy Code canviarà la finestra principal (finestra d'inici) pel nom de la finestra MDI afegida. Llavors, podeu començar a afegir o treure altres finestres depenent de les vostres necessitats. Recordeu que per tal que un objecte Window pugui ser una finestra MDI filla, la seva propietat MDIChild ha de ser igual a TRUE.

Els objectes MDIWindow no accepten cap altre control fill que no siguin els objectes Picture, ToolBar i StatusBar (aquests objectes estan dins de l'MDI frame, no de l'MDI client), per tant, podeu afegir una ToolBar o una StatusBar, o també podeu afegir un objecte Picture amb altres controls a dins, tot i que habitualment, en aquest tipus d'aplicacions, les finestres MDI filles tenen tots els controls necessaris i fan tota la feina. Quan una finestra MDI filla s'activa, el seu menú (si en té algun) s'assigna a la finestra pare (la finestra MDI frame que és un objecte MDIWindow). Si la finestra MDI filla no té menú, la finestra MDI frame continua mostrant el seu (si en té). Tot i que aquest és el funcionament per defecte de les aplicacions MDI, i el més addient, podeu canviar aquest funcionament per a qualsevol finestra MDI filla simplement interceptant el seu missatge WM_MDIACTIVATE i tornant TRUE. Per exemple, suposem que una finestra MDI filla es diu wndMDIChild, per tant, el seu nom de procediment HA DE ser wndMDIChildProcedure. Només cal que afegiu el codi següent (el codi afegit es mostra en lletra negreta):

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 codi d'inicialització va aquí ;
;==================================;
Return (FALSE)
EndU

Aquestes tres línies de codi evitaran el comportament per defecte del menú d'aquesta finestra MDI filla, però el seu menú (si en té un) mai no apareixerà i, per tant, no podreu utilitzar-lo. El sistema operatiu Windows permet que les finestres filles tinguin menú, però no poden mostrar-lo mai per sí mateixes.

D'altra banda, i per tal d'evitar comportaments estranys de les aplicacions MDI, tingueu en compte les següents consideracions:

Quan la finestra principal MDI frame agafa el menú de la finestra MDI filla que està activa, és la finestra MDI frame (l'objecte MDIWindow) qui rep les comandes per aquell menú, no la MDI filla a la que pertany el menú. Aquestes comandes s'envien a través del missatge WM_COMMAND.

Pel missatge WM_COMMAND de la finestra principal MDIWindow, poseu sempre FALSE com a valor de retorn per defecte. Podeu tornar TRUE quan, per exemple, heu processat una comanda de menú, però si torneu sempre TRUE pel missatge WM_COMMAND, es poden produir comportaments inesperats de les finestres MDI filles.

Quan processeu el missatge WM_SIZE d'una finestra MDI filla, torneu SEMPRE FALSE. Si torneu TRUE, no podreu restaurar la finestra MDI filla després de haver-la maximitzat.

Procureu recordar aquestes precaucions. De vegades, en escriure codi, és fàcil canviar el valor de retorn o oblidar-se d'alguna d'aquestes precaucions. El resultat pot ser que la vostra aplicació MDI no funcioni correctament o comenci a comportar-se d'una manera estranya.



AFEGINT CONTROLS FILL

Bé, ara teniu una finestra buida que es pot compilar sense errors, però que no fa res. Per tant, comenceu a dissenyar la vostra aplicació afegint controls fill a les finestres. Per afegir un control, feu clic al botó corresponent de la caixa d'eines (la forma del cursor damunt l'objecte finestra canviarà per una creu). Llavors feu clic amb el botó esquerre del ratolí sobre la finestra, manteniu el botó apretat mentre l'arrosegueu fins que el control tingui les dimensions desitjades, i deixeu anar el botó del ratolí. Recordeu que els objectes Group i Picture també són contenidors, per tant, també podeu posar-hi controls a dins.

També podeu afegir un control fent doble clic sobre el seu botó corresponent a la caixa d'eines. En aquest cas, tingueu present que si l'objecte actiu és un Group o un Picture, el control passarà a ser fill seu. Altrament, serà fill de l'objecte finestra.

L'Easy Code també afegeix el corresponent procediment per cada control que s'afegeix a una finestra (o a un objecte Group o Picture). En la majoria dels casos, amb els missatges WM_COMMAND i WM_NOTIFY, rebuts per la "owner window" del control, n'hi ha prou. Si és així, podeu eliminar els procediments d'aquells controls que no necessiteu processar (els procediments dels controls fill no han d'existir obligatòriament). Quan l'Easy Code no troba el nom de procediment d'un objecte control, simplement l'ignora i llavors no es rebran missatges per aquell objecte, excepte els que s'envien a la "owner window" a través dels missatges WM_COMMAND, WM_NOTIFY i WM_DRAWITEM. D'altra banda, quan sí que vulgueu processar algun missatge en concret d'un objecte control, el seu procediment ha d'existir i el seu nom ha de seguir la regla següent:

Els procediments dels objectes control (qualsevol d'ells) HAN DE tenir un nom que està format pel nom de la "owner window" més el seu propi nom d'objecte.

Per exemple, un control Static anomenat stcLabel que estigui dins d'un objecte finestra anomenat wndMain, HA DE tenir el següent nom de procediment:

wndMainstcLabel (respectant majúscules i minúscules)

A més, els procediments dels objectes control també HAN DE tenir els clàssics quatre paràmetres:

hWnd, uMsg, wParam, lParam

Per tant, el procediment d'un control Static anomenat stcLabel que estigui dins d'un objecte finestra anomenat wndMain serà una cosa així:

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

Aquesta regla per al nom de procediments de controls és comú a qualsevol altre control que afegiu, i és igual si el control està posat directament dins la "owner window" o dins de qualsevol altre control contenidor (Group o Picture).

Aquest és el codi mínim que es necessita pel procediment de qualsevol objecte control. De tota manera, no fa res, per tant, si no heu de processar cap missatge específic d'aquest control, podeu eliminar tot el procediment.

Si el nom del procediment d'un objecte control no existeix o no és correcte, l'Easy Code no dóna cap error, però NO REBREU cap altre missatge per aquest control que no siguin els missatges WM_COMMAND i WM_NOTIFY (o també WM_DRAWITEM si el control és owner draw) a través de la seva "owner window". D'altra banda, com s'ha dit per als objectes finestra, si processeu missatges heu de tornar TRUE per tal que no hi hagi més processament, o FALSE per tal de cridar el procediment per defecte (MAI NO canvieu el valor de retorn per defecte a cap altre valor que no sigui FALSE). Per a qualsevol missatge que s'hagi de processar, afegiu el codi corresponent. Per exemple, si voleu processar el missatge WM_SETFOCUS per un objecte control anomenat stcLabel que està a dins d'un objecte finestra anomenat wndMain, escriviu el codi següent:

wndMainstcLabel Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_SETFOCUS
Jne >
;==========================================;
; El codi del missatge WM_SETFOCUS va aquí ;
;==========================================;

Return (TRUE)
: Return (FALSE)
EndF


OBSERVACIONS: Com s'ha dit abans, l'Easy Code sempre escriu el corresponent procediment per cada control que s'afegeix al projecte. La majoria de vegades aquest procediment no es necessita per res, vist que n'hi ha prou amb processar els missatges WM_COMMAND i WM_NOTIFY que s'envien a la "owner window". Per tant, si no heu de processar cap missatge en concret per un control determinat, podeu eliminar tot el seu procediment (això estalvia alguns bytes al fitxer executable final).



PERSONALITZANT OBJECTES

Quan afegiu objectes ToolBar, StatusBar, TabStrip, ImageList, Header o Rebar podreu personalitzar-los mitjançant la propietat Personalitza, que es troba a la Finestra de propietats i que mostra el quadre de diàleg corresponent. Per a més informació, vegeu els tòpics següents:

Personalitzant objectes ToolBar
Personalitzant objectes StatusBar
Personalitzant objectes TabStrip
Personalitzant objectes ImageList
Personalitzant objectes Header
Personalitzant objectes Rebar



OBTENINT ELS IDENTIFICADORS DELS CONTROLS

En temps d'execució, tots els controls fill que hagiu afegit a cada objecte finestra estaran creats i a punt per a ser utilitzats quan es rep el missatge WM_CREATE de la finestra a la que pertanyen, és a dir, la "owner window" (encara no hi ha cap control creat quan es rep el missatge WM_NCCREATE). Cada control fill té un identificador o ID, amb el seu nom de constant corresponent (tot en majúscules). Per tal de referir-vos a qualsevol control utilitzant aquest nom de constant, una vegada més heu de tenir en compte una simple regla. El nom de constant d'un control és sempre tot en majúscules i està format per 'IDC_', més el nom de la "owner window", més '_', més el nom de l'objecte control. Per exemple, el nom de constant ID per un control Static anomenat stcLabel, que pertanyi a una finestra anomenada wndMain, serà:

IDC_WNDMAIN_STCLABEL

La majoria de les vegades, la "owner window" i el pare del control són el mateix objecte (p.e. quan es posa un control directament a l'objecte finestra), però com hem vist abans, hi ha dos tipus de controls (Group i Picture) que són contenidors i poden tenir controls fill dintre seu. Si és aquest el cas, això NO AFECTA el nom de constant ID, que continua estant format per IDC_, més el nom de la "owner window" (i no el del seu pare, si és un altre), més '_', més el seu propi nom, la qual cosa fa la vostra feina més fàcil no havent de recordar qui és el pare de qui.

Amb aquests noms de constant, és realment fàcil referir-se a qualsevol control al llarg del codi. Això s'aconsegueix utilitzant el mètode de l'Easy Code GetWindowItem. Aquest mètode té dos paràmetres, el "handle" de la "owner window" (o de qualsevol altre objecte control que hi hagi a dins) i el nom de constant del control. Per exemple, dins d'un procediment de finestra (a la que pertanyi el control en qüestió):

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 codi d'inicialització va aquí ;
;==================================;

Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' al
; registre Eax. Feu alguna operació

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

Després de cridar el mètode GetWindowItem, el registre Eax contindrà el "handle" del control referit. Si és fill de qualsevol control contenidor (Group o Picture) o de més d'un (p.e. un objecte control que és fill d'un Group, que és fill d'una Picture, que és filla d'una altra Picture, etc.), el primer argument del mètode GetWindowItem pot ser directament el "handle" de la "owner window" o el "handle" de qualsevol altre control que hi hagi dins la "owner window". Això fa molt fàcil referir-se a qualsevol control dins del procediment d'un altre control (sempre, és clar, que pertanyin a la mateixa "owner window"). Per exemple, suposem que a la mateixa finestra (wndMain) hi tenim un altre control fill que és un objecte Edit anomenat edtEdit, i que dins del seu procediment (que HA DE tenir el nom wndMainedtEdit) volem fer alguna operació amb el control Static anomenat stcLabel:

wndMainedtEdit Frame hWnd, uMsg, wParam, lParam
Cmp D[uMsg], WM_LBUTTONDOWN
Jne > L2
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' al
; registre Eax. Feu alguna operació

Return (TRUE)
L2: Return (FALSE)
EndF

Com que qualsevol dels controls que hi ha a dins de la "owner window" és un "handle" vàlid pel primer argument del mètode GetWindowItem, el "handle" del control edtEdit servirà. Quan el control en qüestió pertany a una altra finestra, podeu utilitzar el mateix mètode i de la mateixa manera, però el primer argument haurà de ser el "handle" de l'altra finestra o de qualsevol control que hi hagi dins l'altra finestra (heu de saber algun "handle"). Si aquesta altra finestra és la finestra principal, sempre podeu obtenir fàcilment el seu "handle" amb App.Main. Per exemple, per obtenir el "handle" d'un control anomenat stcLabel que pertanyi a la finestra principal des del procediment d'un control Edit anomenat edtEdit (que pertanyi a la finestra principal o a qualsevol altra finestra del projecte):

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


OBSERVACIONS: Heu d'anar molt en compte quan passeu el primer paràmetre al mètode GetWindowItem. Passar un "handle" erroni (p.e. un "handle" d'una altra finestra o d'un control fill d'una altra finestra) tornarà un "handle" incorrecte (el "handle" del control fill de l'altra finestra) o NULL.



PROVANT FINESTRES

Després d'afegir alguns controls i modificar les seves propietats, potser voldreu provar la finestra. Si és així, trieu el menú Genera->Prova <NomDeFinestra> o premeu <Shift+F5>. La finestra que es provarà serà la que estigui activa, i es mostrarà de manera modal damunt de l'Easy Code. Quan es prova una finestra no es processa cap codi, però podeu visualitzar com es veurà en temps d'execució, incloënt els menús i recursos de cadena, o la navegació a través dels seus controls amb la tecla <Tab>, per tal de comprovar l'ordre de tabulació (vegeu la propietat TabOrder). Per tancar una finestra que s'està provant, premeu la tecla <Esc>.



Els missatges WM_COMMAND, WM_NOTIFY i WM_DRAWITEM

Els objectes control envien missatges al seu pare. Com ja hem vist abans, un control pot ser fill d'un objecte finestra, d'un altre control que sigui contenidor (objectes Group o Picture), o fins i tot d'un control que estigui dins d'altres controls contenidors. En aquest cas, quin objecte rep els missatges WM_COMMAND, WM_NOTIFY i WM_DRAWITEM? Per tal de fer el més fàcil possible l'escriure codi de manera ràpida (no havent de recordar qui és el pare de qui) aquests missatges els rep SEMPRE la "owner window". Per tant, el codi corresponent s'haurà de posar allà, al missatge WM_COMMAND, WM_NOTIFY o WM_DRAWITEM del procediment de finestra de la "owner window" a la que pertanyi el control.



CREANT OBJECTES FINESTRA EN TEMPS D'EXECUCIÓ

Podeu dissenyar diversos objectes finestra al vostre projecte. Quan l'aplicació s'inicia, i durant tot el temps que estigui en marxa, podeu crear dinàmicament aquestes finestres en resposta a les demandes de l'usuari. Totes les finestres que s'han afegit en temps de disseny estan dins del fitxer executable a punt per a ser utilitzades quan calgui. Per tal de crear-les (vegeu l'exemple MDI que ve amb l'Easy Code), utilitzeu el mètode Create. Aquest mètode torna el "handle" de la nova finestra creada (si no és modal), o el valor de retorn (si és modal), al registre Eax. La seva sintaxi és la següent:

Invoke Create, lpszWindowName, hWndParentWindowHandle, lMode, lParam

El paràmetre lpszWindowName és un valor DD amb el punter de l'adreça efectiva d'una cadena de text acabada en zero, que conté el nom de l'objecte finestra (el que s'indica a la seva propietat Name, tenint en compte les majúscules i minúscules).

El paràmetre hWndParentWindowHandle és un valor DD amb el "handle" de la finestra pare. Aquest valor pot ser NULL (excepte per les finestres MDI filles) si la finestra que es vol crear no ha de tenir pare.

El paràmetre lMode és un valor DD que indica si la finestra creada s'ha de mostrar de manera modal o no.

El paràmetre lParam és el valor DD que es passa als quadres de diàleg a través del paràmetre lParam del missatge WM_INITDIALOG. Aquest valor només és pels objectes DialogBox, modals o no, i pot ser NULL si no es necessita.

L'Easy Code té dues constants pel paràmetre lMode, ecModal i ecModeless. De fet, els seus valors són, respectivament, 1 (TRUE) i 0 (FALSE), per tant, qualsevol altre valor que no sigui 0 voldrà dir TRUE, és a dir, una finestra modal. Per evitar errors no desitjats, utilitzeu sempre les constants ecModal i ecModeless.

Els objectes Window (sempre que tinguin la seva propietat MDIChild igual a FALSE) i DialogBox poden ser modals o no, depenent del tercer paràmetre del mètode Create, mentre que els objectes Window (que tinguin la seva propietat MDIChild igual a TRUE) i MDIWindow només poden ser no modals i, per tant, els paràmetres tercer i quart són ignorats. De fet, el tercer paràmetre (lMode) només és vàlid per als objectes Window i DialogBox, mentre que el quart paràmetre (lParam) només és vàlid per als objectes DialogBox.

Per a les aplicacions MDI, l'Easy Code té el mètode GetMDIClient per obtenir el "handle" del client MDI. De tota manera, quan es creen finestres MDI filles, el "handle" que heu de passar al mètode Create com a finestra pare (segon paràmetre) és el de la finestra MDI frame (l'objecte MDIWindow), i no el que torna el mètode GetMDIClient.

Bé, ara suposem que tenim un projecte amb els objectes següents (els noms dels objectes es mostren de color blau):

Un objecte principal Window anomenat wndMain i un objecte DialogBox anomenat dlgOptions. Dins de la finestra principal (wndMain), hi tenim un objecte Button amb el nom de btnShow. Volem que la DialogBox (dlgOptions) es mostri de manera modal quan fem clic a l'objecte Button (btnShow), i que la finestra principal (wndMain) en sigui el pare. A l'objecte DialogBox (dlgOptions), hi tenim dos objectes Button anomenats btnOK i btnCancel, i tots dos tanquen el quadre de diàleg modal (btnOK el tanca validant i btnCancel el tanca cancel·lant).

El nom del procediment de la finestra principal ha de ser wndMainProcedure.
El nom de l'objecte DialogBox que s'ha de crear és dlgOptions.
L'ID del control Button (btnShow) ha de ser IDC_WNDMAIN_BTNSHOW (perquè pertany a l'objecte wndMain i el seu nom és btnShow).

El nom del procediment del quadre de diàleg ha de ser dlgOptionsProcedure.
L'ID del control Button (btnOK) ha de ser IDC_DLGOPTIONS_BTNOK (perquè pertany a l'objecte dlgOptions i el seu nom és btnOK).
L'ID del control Button (btnCancel) ha de ser IDC_DLGOPTIONS_BTNCANCEL (perquè pertany a l'objecte dlgOptions i el seu nom és btnCancel).

La "owner window" és l'objecte que rep els missatges WM_COMMAND enviats per qualsevol dels seus controls fill (en aquest exemple els missatges dels clics del botó btnShow). Per tant, dins del missatge WM_COMMAND (rutina OnCommand) del procediment de la finestra principal (wndMainProcedure), hi escriurem el codi següent:

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 codi d'inicialització 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 codi final va aquí ;
;=======================;
Invoke IsModal, hWnd
Or Eax, Eax
Jz >
Invoke EndModal, hWnd, IDCANCEL
Mov Eax, TRUE
: Ret
EndU


mentre que dins del missatge WM_COMMAND (rutina OnCommand) del quadre de diàleg (dlgOptionsProcedure) hi escriurem el codi següent:

dlgOptionsProcedure Frame hDlg, uMsg, wParam, lParam
Cmp D[uMsg], WM_INITDIALOG
Jne >
;=====================================;
; Aquí lParam té el valor passat al ;
; mètode Create com a quart paràmetre ;
;=====================================;

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 codi d'inicialització 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 codi final va aquí ;
;=======================;

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

LoWord i HiWord són macros de l'Easy Code que tornen, respectivament, la paraula baixa ("low word") i la paraula alta ("high word"), valors de 16 bits (o DW) d'un valor de 32 bits (o DD) que es passa com a argument (en aquest exemple wParam). El valor de 16 bits es torna al registre Eax (de fet, es troba al registre Ax). TEXT és una altra macro de l'Easy Code que crea una cadena de text local i temporal (en aquest exemple amb el text "dlgOptions", que és el nom de l'objecte DialogBox). No cal cap caràcter NULL al final de la cadena perquè la macro TEXT l'afegeix internament. Finalment, al missatge WM_COMMAND de la "owner window" que rep els clics dels botons btnOK i btnCancel (l'objecte DialogBox), cridem un altre mètode de l'Easy Code, EndModal, que tanca (destrueix) una finestra modal i torna el valor indicat (en aquest exemple IDOK o IDCANCEL). El mètode EndModal té dos paràmetres, el "handle" de la finestra que s'ha de destruir i el valor de retorn. Aquest mètode torna TRUE o FALSE, al registre Eax, depenent de si la finestra s'ha destruït o no (p.e. si crideu aquest mètode per un objecte finestra que no és modal, no farà res i tornarà FALSE), i es pot cridar des de qualsevol part del codi. Una vegada s'ha cridat, la finestra modal ha estat definitivament destruïda. És per això que cal tornar TRUE després de cridar el mètode EndModal, per tal que no hi hagi més processament pel missatge WM_CLOSE.

Una altra manera de tancar una finestra modal amb IDCANCEL com a valor de retorn, és enviar un missatge WM_CLOSE. A l'exemple que hem vist, el codi de l'objecte Button per cancel·lar (btnCancel) dins del missatge WM_COMMAND (rutina OnCommand) del procediment del quadre de diàleg (dlgOptions), es podria modificar pel codi següent (el text modificat es mostra en lletra negreta):

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

Noteu que després d'enviar el missatge WM_CLOSE, heu de tornar TRUE per tal que no hi hagi més processament pel missatge WM_COMMAND, perquè la finestra ja ha estat destruïda (no existeix) i continuar processant el missatge podria produir greus errors. D'altra banda, quan processeu el missatge WM_CLOSE per una finestra modal, i només si la finestra és modal, podeu treure algunes línies de codi:

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

I això és tot el que necessiteu per crear un objecte finestra en temps d'execució, que hagi estat dissenyada en temps de disseny. Fàcil, oi?

IMPORTANT: El mètode EndModal només funciona amb finestres modals. Si l'invoqueu amb el "handle" d'una finestra no modal, el mètode tornarà FALSE i la finestra no serà destruïda. D'altra banda, després de cridar el mètode EndModal amb un "handle" vàlid (el d'una finestra modal), la finestra ja estarà destruïda, per tant, heu de tornar TRUE per tal que no hi hagi més processament perquè l'objecte ja no existeix. Altrament, poden produir-se errors.



El missatge WM_CREATE

Quan una finestra rep aquest missatge, tots els controls fill que li hagiu afegit en temps de disseny ja estan creats i disponibles (utilitzeu el seu nom de constant ID per a qualsevol operació). Com s'ha dit abans, aquest missatge ha de tornar qualsevol valor diferent de -1 per tal de continuar la creació de la finestra (un valor de retorn FALSE o TRUE serà correcte). Si torna TRUE, el focus s'assigna al primer control que pugui tenir-lo (vegeu la propietat TabOrder). La majoria de les vegades, aquest és el valor de retorn més habitual. Contràriament, si assigneu el focus a qualsevol altre control durant el processament d'aquest missatge (funció de l'API SetFocus), haureu de tornar FALSE per tal que el focus continüi assignat al control especificat. Finalment, recordeu que un valor de retorn de -1 destruïrà la finestra abans de que sigui visible.

OBSERVACIONS: Els Quadres de diàleg no reben el missatge WM_CREATE. En el seu lloc, reben el missatge WM_INITDIALOG amb el paràmetre lParam posat al valor que s'hagi passat al mètode Create com a quart paràmetre. Aquest paràmetre pot ser NULL si no es necessita.



El missatge ECM_AFTERCREATE

Just després de crear una finestra, i abans de que sigui visible, l'Easy Code envia el missatge ECM_AFTERCREATE al procediment de la finestra. Aquest missatge pot ser útil per tal de fer algun tipus d'inicialització o canviar allò que no s'hagi pogut dur a terme durant el missatge WM_CREATE. En aquest punt, la finestra i tots els seus fills estan completament creats i a punt de ser mostrats. El missatge ECM_AFTERCREATE és exclusiu de l'Easy Code, el seu valor és WM_USER + 1049 i la seva sintaxi és corregida automàticament a majúscules per l'IDE.

OBSERVACIONS: El valor de retorn d'aquest missatge indica si la finestra s'ha de destruir o no. Si aquest valor és -1, la finestra serà destruïda abans de ser mostrada, mentre que qualsevol altre valor permetrà que el procés continüi.



El missatge WM_CLOSE

Un objecte Window (que no sigui una finestra MDI filla) i un objecte DialogBox poden ser modals o no. Quan es crea una finestra modal, el seu pare (si en té) és deshabilitat i no té cap control fins que es tanca la finestra modal. En aquest punt, s'espera un valor de retorn que ens digui si l'usuari ha validat o cancel·lat les opcions que se li oferien a la finestra modal. Aquest valor (habitualment IDOK o IDCANCEL) es troba al registre Eax quan el mètode Create torna, i és el valor que s'ha passat al mètode EndModal quan s'ha destruït la finestra modal.

D'altra banda, quan es creen finestres no modals, el mètode Create torna immediatament amb el "handle" de la nova finestra creada al registre Eax. Si la finestra no s'ha pogut crear per qualsevol motiu (per exemple si el nom no existeix o no és correcte, perquè recordeu que els noms d'objecte diferencien les majúscules de les minúscules), llavors el valor de retorn serà NULL.

En tots dos casos, podeu evitar que la finestra sigui destruïda tornant TRUE al missatge WM_CLOSE (p.e. si demaneu confirmació), mentre que tornant FALSE la finestra es tancarà (destruïrà) normalment. A les finestres modals, qualsevol confirmació s'ha de demanar abans de cridar el mètode EndModal, tenint en compte que després de cridar-lo la finestra ja està destruïda. Per tant, tot el codi per evitar que es tanqui i destrueixi un objecte finestra s'ha de posar al missatge WM_CLOSE. Si, per exemple, heu demanat confirmació per tancar i l'usuari ha contestat que no, simplement torneu TRUE. A les finestres modals, torneu també TRUE però sense cridar el mètode EndModal.

Quan afegiu objectes Window i DialogBox al projecte, com que tots dos poden ser modals o no, l'Easy Code escriu el codi necessari al missatge WM_CLOSE per tal que la finestra es tanqui pròpiament, tant si és modal com si no. Aquest codi (en lletra negreta) és el següent:

OnClose:
UseData wndMainProcedure
;=======================;
; El codi final va aquí ;
;=======================;

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

: Ret
EndU

Quan es tanca la finestra, es crida un altre mètode de l'Easy Code, IsModal, que torna TRUE o FALSE al registre Eax, depenent de si la finestra és modal o no. Si és modal, s'ha de cridar el mètode EndModal per tal de tancar correctament, mentre que si no és modal, la finestra es tanca normalment tornant FALSE.

OBSERVACIONS: El missatge WM_CLOSE és l'última oportunitat d'evitar que la finestra sigui destruïda. Després d'això, rebreu els missatges WM_DESTROY i WM_NCDESTROY, però només per fer neteja (alliberar recursos utilitzats, etc.). En el moment d'aquests dos últims missatges, ja no hi ha res a fer. La finestra s'està destruïnt!

IMPORTANT: Aneu en compte! Tornar sempre TRUE per un missatge WM_CLOSE, significa que la finestra no es destrueix mai. Si és una finestra modal, i no crideu el mètode EndModal, la vostra aplicació no es podrà tancar mai de manera correcta.



QUADRES DE DIÀLEG

Els quadres de diàleg ("dialog boxes") s'utilitzen feqüentment a les aplicacions Windows per intercanviar informació amb l'usuari. Poden ser modals o no i es creen a partir d'una plantilla, habitualment del fitxer de recursos del programa. Per tal de calcular les dimensions d'un quadre de diàleg, el Windows utilitza el promig de l'alçada i l'amplada de la seva font. Tots els controls que hi ha dins d'un quadre de diàleg utilitzen la mateixa font que el seu pare (el quadre de diàleg), per tant, no poden tenir la seva pròpia font com a qualsevol altra finestra. Utilitzar només una font per a tots els objectes, permet que el quadre de diàleg i els seus fills es mostrin sempre proporcionalment en diferents resolucions de pantalla i de fonts. Quan es canvia la propietat Font d'un quadre de diàleg en temps de disseny, ell i tots els seus controls fill són redimensionats d'acord amb la grandària de la nova font, mentre que si es canvia la font en temps d'execució, només canvia la font, però no es porta a terme cap redimensió.

Els projectes visuals tenen el seu propi quadre de diàleg, l'objecte DialogBox, que ofereix algunes prestacions més que els quadres de diàleg dels projectes clàssics. Tots els objectes DialogBox que necessita una aplicació, dissenyats a l'entorn visual (temps de disseny), poden ser creats en temps d'execució cridant el mètode Create. Un cop creat, un objecte DialogBox (modal o no) no rep mai el missatge WM_CREATE, enlloc d'això rep el missatge WM_INITDIALOG, abans que la finestra sigui visible, on es pot escriure tot el codi necessari. Quan es rep aquest missatge, el paràmetre lParam té el valor que s'hagi passat com a quart paràmetre al mètode Create. Aquest paràmetre pot ser qualsevol valor que calgui passar-li al quadre de diàleg, per exemple l'adreça d'una estructura de dades, o pot ser NULL si no es necessita.

El valor de retorn d'un missatge WM_INITDIALOG té un significat especial (també aplicat al missatge WM_CREATE dels objectes Window). És el següent:

Si torna TRUE, el focus s'assigna al primer control que pugui tenir-lo (vegeu la propietat TabOrder). Majoritàriament, aquest és el valor de retorn habitual.

Si assigneu el focus a qualsevol altre control durant l'execució d'aquest missagte, cridant la funció de l'API SetFocus, haurieu de tornar FALSE per tal que el focus continüi assignat al control especificat.

Quan destruïu una finestra modal (sigui un objecte DialogBox o Window), cal que crideu el mètode de l'Easy Code EndModal. Aquesta és la manera correcta de destruir una finestra modal. No crideu MAI la funció de l'API DestroyWindow si la finestra que s'ha de destruir és modal.



PROCESSANT EL TEMPS "IDLE"

Quan l'aplicació no té cap missatge per processar, es diu que està idle i no fa altra cosa que esperar que li arribin més missatges. Podeu aprofitar aquest temps idle per tal de portar a terme algun tipus de processament en segon pla. Si és així, només heu d'escriure el següent procediment:

OnIdle Frame lCount
Return (FALSE)
EndF

El nom d'aquest procediment HA DE ser OnIdle (respectant majúscules i minúscules), ha d'estar a la finestra d'inici i es crida quan no hi ha cap missatge per processar. El paràmetre lCount s'incrementa cada vegada que es crida OnIdle dins del mateix cicle i es posa a 0 cada vegada que es processa un nou missatge, per tant, podeu saber quan comença un nou cicle idle comprovant que el valor del paràmetre lCount sigui 0. Basant-vos en aquest comptador, podeu cridar diferents rutines idle:

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

Aquest procediment ha de tornar TRUE per tal de continuar rebent més temps de processament "idle" dins del mateix cicle. Altrament, si torna FALSE, el procediment ja no es cridarà més durant el cicle actual, és a dir, no tornarà a ser cridat fins al pròxim cicle "idle".

És molt important tenir en compte el comportament del procediment OnIdle per tal de no sobrecarregar el processador. Si aquest procediment torna sempre TRUE, el processador estarà permanentment sobrecarregat, per tant, una bona pràctica és portar a terme diferents petites tasques fins que lCount arribi a un valor determinat (50 a l'exemple anterior).


OBSERVACIONS: El procediment OnIdle s'utilitza per portar a terme tasques simples en segon pla. Les tasques més llargues s'haurien de dividir en moltes petites rutines que es cridarien seqüencialment en funció del paràmetre lCount.



ACCEDINT A LA FUNCIÓ WinMain DEL WINDOWS (QUAN L'APLICACIÓ S'INICIA I/O FINALITZA)

També podeu obtenir el control quan l'aplicació s'inicia i/o finalitza (directament des de l'inici i el final de la funció WinMain), simplement afegint, respectivament, els procediments MainStart i/o MainEnd (respectant majúscules i minúscules) a la secció .Code de la finestra principal (finestra d'inici) de l'aplicació:

MainStart Frame hInstance, hPrevInst, lpCmdLine, nCmdShow
	; Escriviu el vostre codi aquí
Ret
EndF

MainEnd Frame hInstance, hPrevInst, lpCmdLine, nCmdShow ; Escriviu el vostre codi aquí
Ret
EndF

Quan la funció WinMain s'inicia, crida al procediment MainStart (si existeix) i quan la funció WinMain finalitza, crida al procediment MainEnd (si existeix). Cal que tingueu molta cura amb el codi que escriviu en aquests dos procediments (especialment el primer), atès que podeu bloquejar l'aplicació. En tots dos procediments, el valor de retorn és indiferent.




CONTROLANT EL BUCLE DE MISSATGES DE L'APLICACIÓ

En molts casos n'hi ha prou amb processar els missatges dins dels procediments dels objectes. Tot i així, si necessiteu més control i voleu comprovar els missatges abans de que siguin processats, afegiu el codi següent a la secció .Code de la finestra principal (finestra d'inici) de l'aplicació.

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

El nom d'aquest procediment HA DE ser ProcessMessages (respectant majúscules i minúscules) i es crida directament des del bucle de missatges de l'aplicació, abans que cap altra funció de l'API. Si ProcessMessages existeix, és a dir, si el seu procediment està present, HA DE tenir el nom indicat i estar a la finestra principal (finestra d'inici) de l'aplicació, per tal que l'Easy Code pugui cridar-lo a cada missatge.

ProcessMessages us permet comprovar l'objecte de destinació (hWnd), el missatge (uMsg) i els dos paràmetres del missatge (wParam i lParam). Si torneu TRUE, el missatge mai no serà processat. Com podeu veure, aquest procediment us dóna un control total sobre l'aplicació. Suposem que no voleu que es processi el missatge WM_KEYDOWN per un objecte Static anomenat stcLabel, que pertany a la finestra principal de l'aplicació (anomenada wndMain). El nom de constant ID per l'objecte stcLabel serà IDC_WNDMAIN_STCLABEL (perquè està dins de wndMain i el seu nom és stcLabel).

Només heu d'escriure aquest codi:

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

En aquest exemple, podem saber fácilment el "handle" de stcLabel perquè està dins de la finestra principal (App.Main sempre conté el "handle" de la finestra principal). Altrament, hauríem de saber quina és la "owner window" del control que volem comprovar.

A la majoria d'aplicacions aquest procediment no es necessita per res. Com que vosaltres decidiu quina és la finestra principal (on ha d'estar aquest procediment), i si utilitzareu ProcessMessages o no, l'Easy Code no l'afegeix mai per defecte.

IMPORTANT: Aneu amb molt de compte amb aquest procediment i no canvieu mai el valor de retorn per defecte FALSE. Com que tornar TRUE significa que el missatge no es processa (s'ignora), si sempre torna TRUE (és a dir, qualsevol altre valor que no sigui FALSE), voldrà dir que no es processarà cap missatge i la vostra aplicació es bloquejarà.



PRESERVANT ELS REGISTRES ALS PROJECTES VISUALS

Quan treballeu amb projectes visuals, podeu utilitzar lliurament els registres Ebx, Ecx, Edx, Edi, i Esi a tots els procediments del projecte sense desar-los (excepte per la vostra conveniència quan necessiteu desar-ne algun), perquè l'Easy Code ja se'n cuida de fer-ho. D'altra banda, quan crideu qualsevol mètode de l'Easy Code, podeu confiar en que cap d'aquests registres serà modificat.



OBJECTES - PROPIETATS I MÈTODES

Cada objecte, finestra o control, té unes propietats que modifiquen la seva aparença i funcionament. Modifiicant aquestes propietats i movent i redimensionant els controls, podeu dissenyar fàcilment una aplicació a l'entorn de desenvolupament Easy Code. Al llarg de tot el codi, dins dels procediments de cada objecte, podeu modificar propietats i cridar mètodes. El codi que escriviu allà serà portat a terme en temps d'execució (quan l'aplicació estigui en marxa). Per això, l'Easy Code té diversos mètodes molt útils per intercanviar informació amb els objectes i portar a terme operacions diverses. Cal que us familiaritzeu amb totes aquestes propietats i mètodes, i programar una gran aplicació Windows de 32 bits en assemblador serà fàcil, ràpid i possible.

Propietats dels objectes
Llegint i escrivint propietats
Mètodes de l'Easy Code

Per tal d'il·lustrar tot el que s'ha dit aquí, vegeu els exemples que vénen amb l'aplicació Easy Code (ubicats a la carpeta Examples del directori de l'Easy Code). Les aplicacions File Shredder, MIDI Player, MDI, RGG i TabStrip (projectes visuals) han estat programades totalment amb l'Easy Code i utilitzen tot l'estil de programació que s'ha vist en aquest capítol.