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 (ECDllMsd.dll o ECDllMsr.dll) que s'ha de distribuir amb l'aplicació, mentre que el mode estàtic inclourà la llibreria visual necessària (ECStcMsd.lib o ECStcMsr.lib) 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 dins 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 GetOwnerWindow.
Les lllibreries visuals de l' Easy Code, ECStcMsd.lib o ECStcMsr.lib per enllaç estàtic, i ECDllMsd.dll o ECDllMsr.dll per enllaç dinàmic (modes Debug i Release, respectivament), 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 inc i lib (dels subdirectoris include i lib del Masm32 o del GeneSys):
windows.inc
gdi32.inc
kernel32.inc
user32.inc
advapi32.inc
gdi32.lib
kernel32.lib
user32.lib
advapi32.lib
Per tant, no necessiteu incloure cap d'aquests fitxers al vostre projecte, per què si ho feu seran ignorats. La majoria de projectes només necessiten aquests fitxers o fins i tot algun menys. Si voleu incloure el fitxer Macros.asm del Masm32, simplement marqueu l'opció corresponent (Fitxer Macros.asm) a les Propietats del projecte.
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:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
Fixeu-vos que els valors HWND,
ULONG,
WPARAM
i LPARAM
són de 32 bits, per tant, es poden canviar per valors DWord.
És decisió vostra, però d'aquesta
manera el codi és més clar i preparat per
a futurs canvis.
Per tant, el procediment d'un objecte finestra anomenat wndMain serà més o menys així:
wndMainProcedure Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
;......
;El codi d'inicialització va aquí...
;······
Return FALSE
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMainProcedure EndP
Noteu que, com que l'Easy Code ignora la directiva Option Proc, els procediments són sempre Public per defecte si no són explícitament declarats com a Private. Vegeu la secció Codi i Dades: Privat o Públic.
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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
;......
;El codi d'inicialització va aquí...
;······
Return FALSE
.ElseIf uMsg == WM_SIZE
;......
;El codi del missatge WM_SIZE va aquí...
;······
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMainProcedure EndP
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.
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 EC Player, File Shredder i MIDI Player.
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).
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 comportament 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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
;......
;El codi d'inicialització va aquí...
;······
Return FALSE
.ElseIf uMsg == WM_MDIACTIVATE
Return TRUE
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMDIChildProcedure EndP
Aquestes dues 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.
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:HWND, uMsg:ULONG, wParam:WPARAM, lParam: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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
Return FALSE
wndMainstcLabel EndP
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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_SETFOCUS
;......
;El codi del missatge WM_SETFOCUS va aquí...
;······
Return TRUE ;(o FALSE, vosaltres decidiu)
.EndIf
Return FALSE
wndMainstcLabel EndP
En aquest exemple, el valor de retorn del missatge WM_SETFOCUS és TRUE. Si voleu tornar FALSE, simplement no escriviu cap sentència de retorn atès que el codi tornarà FALSE (valor de retorn per defecte) quan arribi al final del procediment:
wndMainstcLabel Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_SETFOCUS
;......
;El codi del missatge WM_SETFOCUS va aquí...
;······
.EndIf
Return FALSE
wndMainstcLabel EndP
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).
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ó, vejeu 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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' al
; registre Eax. Feu alguna operació
Return FALSE
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMainProcedure EndP
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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_LBUTTONDOWN
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' al
; registre Eax. Feu alguna operació
Return TRUE
.EndIf
Return FALSE
wndMainedtEdit EndP
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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_LBUTTONDOWN
Invoke GetWindowItem, App.Main, IDC_WNDMAIN_STCLABEL
; "Handle" del control 'stcLabel' al
; registre Eax. Feu alguna operació
Return TRUE
.EndIf
Return FALSE
wndMainedtEdit EndP
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.
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>.
Com que l'Easy Code ignora la directiva Option Proc, els procediments són sempre Public per defecte si no es declaren explícitament com a Private. Vegeu la secció Codi i Dades: Privat o Públic.
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 el fitxer executable a punt per a ser utilitzades quan calgui. Per tal de crear-les (vegeu l'exemple EC Player 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 LPSTR (DWord) 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 se indica a la propietat Name, tenint en compte les majúscules i minúscules).
El paràmetre hWndParentWindowHandle és un valor HWND (DWord) 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 LONG (DWord) que indica com s'ha de mostrar la finestra, de manera modal o no.
El paràmetre lParam és el valor LPARAM (DWord) que es passa als quadres de diàleg a través del paràmetre lParam del missatge WM_INITDIALOG. Aquest valor només és per als 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
del procediment de la finestra principal (wndMainProcedure),
hi
escriurem el codi següent:
wndMainProcedure Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; ....
; El codi d'inicialització va aquí
; ....
Return FALSE
.ElseIf uMsg == WM_COMMAND
LoWord wParam
.If Ax == IDC_WNDMAIN_BTNSHOW
HiWord wParam
.If Ax == BN_CLICKED
Invoke Create, TextAddr("dlgOptions"), hWnd, ecModal, NULL
Return TRUE
.EndIf
.EndIf
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMainProcedure EndP
mentre que dins del missatge WM_COMMAND
del quadre de diàleg (dlgOptionsProcedure)
hi escriurem el codi següent:
dlgOptionsProcedure Proc hDlg:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_INITIDIALOG
Invoke GetWindowItem, hWnd, IDC_WNDMAIN_STCLABEL
; Aquí lParam té el valor que hagiu passat
; al mètode Create com a quart paràmetre
;......
; El codi d'inicialització va aquí...
;······
Return FALSE
.ElseIf uMsg == WM_COMMAND
LoWord wParam
.If Ax == IDC_DLGOPTIONS_BTNOK
HiWord wParam
.If Ax == BN_CLICKED
Invoke EndModal, hDlg, IDOK
Return TRUE
.EndIf
.ElseIf Ax == IDC_DLGOPTIONS_BTNCANCEL
HiWord wParam
.If Ax == BN_CLICKED
Invoke EndModal, hDlg, IDCANCEL
Return TRUE
.EndIf
.EndIf
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hDlg
.If Eax
Invoke EndModal, hDlg, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
dlgOptionsProcedure Endp
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 Word) d'un valor de 32 bits (o DWord) que es passa com a argument (en aquest exemple wParam). El valor de 16 bits es torna al registre Eax (de fet, el valor es troba al registre Ax). TextAddr é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 TextAddr 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 el 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 del procediment del quadre de diàleg (dlgOptions), es podria modificar pel codi següent (el text modificat es mostra en lletra negreta):
.ElseIf uMsg == WM_COMMAND
LoWord wParam
.If Ax == IDC_DLGOPTIONS_BTNOK
HiWord wParam
.If Ax == BN_CLICKED
Invoke EndModal, hDlg, IDOK
Return TRUE
.EndIf
.ElseIf Ax == IDC_DLGOPTIONS_BTNCANCEL
HiWord wParam
.If Ax == BN_CLICKED
Invoke SendMessage, hDlg, WM_CLOSE, 0, 0
Return TRUE
.EndIf
.EndIf
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hDlg
.If Eax
Invoke EndModal, hDlg, IDCANCEL
Return TRUE
.EndIf
.EndIf
Noteu que després d'enviar el missatge WM_CLOSE, heu de tornar TRUE per tal que no hi hagi més processament per al missatge WM_COMMAND, atès que 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:
.ElseIf uMsg == WM_CLOSE
Invoke EndModal, hDlg, IDCANCEL
Return TRUE
.EndIf
Return FALSE
I això és tot el que necessiteu per a 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.
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.
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.
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:
wndMainProcedure Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_CREATE
;......
;El codi d'inicialització va aquí...
;······
Return FALSE
.ElseIf uMsg == WM_CLOSE
Invoke IsModal, hWnd
.If Eax
Invoke EndModal, hWnd, IDCANCEL
Return TRUE
.EndIf
.EndIf
Return FALSE
wndMainProcedure EndP
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.
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.
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 Proc lCount:LONG
Return FALSE
OnIdle EndP
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 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 Proc lCount:LONG
.If lCount == 0
; Feu alguna tasca
.ElseIf lCount == 1
; Feu alguna altra tasca
.ElseIf lCount >= 50
Return FALSE
.EndIf
Return TRUE
OnIdle EndP
Aquest procediment ha de
tornar TRUE
per tal de
continuar rebent més temps de processament "idle".
Altrament, si torna FALSE,
el procediment ja no es cridarà més durant el
cicle actual, és a dir, no es tornarà a cridar
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 Proc hInstance:HINSTANCE, hPrevInst:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:DWord
; Escriviu el vostre codi aquí
Ret
MainStart EndP
MainEnd Proc hInstance:HINSTANCE, hPrevInst:HINSTANCE, lpCmdLine:LPSTR, nCmdShow:DWord
; Escriviu el vostre codi aquí
Ret
MainEnd EndP
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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
Return FALSE
ProcessMessages EndP
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 de 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 Proc hWnd:HWND, uMsg:ULONG, wParam:WPARAM, lParam:LPARAM
.If uMsg == WM_KEYDOWN
Invoke GetWIndowItem, App.Main, IDC_WNDMAIN_STCLABEL
; "Handle" de 'stcLabel' al registre Eax
.If Eax == hWnd
Return TRUE
.EndIf
.EndIf
Return FALSE
ProcessMessages EndP
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
EC Player,
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.