L'objectif içi n'est pas de créer un cas d'école,mais du code réutilisable et modifiable.Les sources d'informations sont le SDK et les articles en C++ de MSDN.Une fois lu,vous pourrez
On utilise deux points:
point origine(0,0) coin gauche haut de l'écran
(X left,Y top)
(X right,Y bottom)
point coin droit bas
Windows utilise la structure suivante pour définir la position des fenêtres
RECT STRUCT
left dd ?
top dd ?
right dd ?
bottom dd ?
RECT ENDS
Pour définir la position d'une fenêtre windows , la structure RECT (rectangle) définit les points comme ci dessus
L'origine étant en haut à gauche , pour obtenir la hauteur et la largeur en nombre positif , il faut:
largeur = RECT.right - RECT.left hauteur=RECT.bottom - RECT.top
Deux fonctions renseignent les valeurs de RECT:
GetWindowRect fournit les coordonnées externes de la fenêtre.Toutes les coordonnées externes démarrent du coin gauche haut de lécran.
GetClientRect fournit les dimensions internes des fenêtres,donnant l'espace disponible pour les contrôles.Seul right et top (coin droit bas) sont différent de zéro (pour un décalage Nul) et donnent donc directement largeur et hauteur disponible.
Pour obtenir ces informations , il faut
Déclarer une variable à
la dimension de RECT: LOCAL rect:RECT
ou rect RECT <>
appeler la fonction invoke
GetWindowRect,handle,addr rect
Lorsqu'on veut créer ou déplacer des contrôles à l'intérieur d'une fenêtre , les coordonnées deviennent relatives au bord haut gauche de la feuille parente (on n'utilise que le point origine) et deviennent:
(X,Y),largeur,hauteur aucune structure n'est définie pour les contenir
ou X=left et Y= top, mesure le décalage du point origine
CreateWindowEx pour créer les
fenêtres ou les controles
MoveWindow pour déplacer la
fenêtre ou les contrôles
invoke
MoveWindow,Handle,X,Y,largeur,hauteur,FALSE
Les valeurs maximums des axes vont être définis par la
résolution de l'écran,exemple 800,600
GetSystemMetrics,SystemParametersInfo permettent
de retrouver ces dimensions
Utilisation:
Pour positionner les contrôles et les fenêtres , il va falloir jongler avec des nombres a 3 chiffres sans aucune signification et surtout ne pas changer de résolution d'écran.L'utilisation de nombres comme 700,200,10,55 devient vite illisible si le nombre de contrôles dépasse le nombre des doights d'une main.Au premier essai , on obtient un tas plus ou moins éparpillé de fenêtres et de contrôles.L'utilisation de quelques variables littérales finit d'éclaircir le problème.
La solution consiste à exprimer les coordonnées en pourcentage de résolution de l'écran
Longueur et hauteur de l'écran deviennent égale à 100 , les dimensions à la visualisation restent inchangés quelque soit la résolution de l'écran , les calculs deviennent à 2 chiffres.Il est facile de mémoriser quelques valeurs de dimensions.Un gros bouton fait 9 de haut , une marge 2 ...
Pratique:
Télécharger l'exemple
La transformation en pourcentage doit s'accomplir avec une certaine précision et l'utilisation du coprocesseur mathématique (FPU Floating Point Unit) s'impose.La fonction GetPercent fournie avec la librairie de masm32 semble résoudre le problème.Hélas , son auteur à oublier que le FPU est sujet au débordement de pile (elle ne peut contenir que 8 nombres flottants st(0) à st(7)) et au bout de 2 ou 3 calculs , ne répond plus que 0.Voici sa version corrigée.
PourCent proc source:DWORD, percent:DWORD
LOCAL var1:DWORD
mov var1, 100 ; to divide by 100
FINIT ; l'instruction qui manque dans lib masm32
fild source ; load source integer
fild var1 ; load 100
fdiv ; divide source by 100
fild percent ; load required percentage
fmul ; multiply 1% by required percentage
fist var1 ; store result in variable
mov eax, var1
ret
PourCent endp
Démarrer l'éxécution du programme fenêtre sous plusieurs résolutions , vous pourrez constater qu'il conserve les mêmes dimensions.Télécharger l'exemple
Disparition complète:
On peut facilement voire disparaitre complétement une fenêtre de l'écran , en utilisant incorrectement les styles,en voici un exemple:
;==================================================
; rempli la structure WNDCLASSEX
;==================================================
mov wc.cbSize, sizeof WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW \
or CS_BYTEALIGNWINDOW
mov wc.lpfnWndProc, offset WndProc ;prend un nom indifferent
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
PuPo wc.hInstance, hInst ;<< NOTE: macro not mnemonic
mov wc.hbrBackground, COLOR_BTNFACE+1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, offset szClassName
invoke LoadIcon,hInst,500 ; icon ID
mov wc.hIcon, eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor, eax
mov wc.hIconSm, 0
invoke RegisterClassEx, ADDR wc
CS_BYTEALIGNWINDOW est le style en trop , et la fenêtre disparait .Rien n'indique dans winhelp la possibilité d'obtenir un tel résultat:
CS_BYTEALIGNWINDOW Aligns a window on a byte boundary (in the x direction) to enhance performance during operations that involve moving or sizing the window. This style affects the width of the window and its horizontal position on the display.
Fenêtre aux contours Flous ,palissant ,s'évanouissant.
Un controle d'édition peut avoir pour parente des tab,ayant pour parente la feuille mére
Lorsque qu'une feuille parente rafraichi son affichage à l'aide de l'évènement PAINT elle rafraichi en même temps ses filles.C'est ce qui occasionne les problèmes d'effacement lorsque la fille utilise son propre évènement paint.La propriété WS_CLIPCHILDREN permet d'empécher la parente de rafraichir en même temps la fille.Encore faut-il appliquer la propriété à toutes les parentes de la feuille fille
Dans le cas qui nous occupe:
La fenêtre principale doit posséder la propriété
WS_CLIPCHILDREN
Le contrôle tab doit posséder la propriété
WS_CLIPCHILDREN
Le controle d'édition doit posséder la propriété
WS_CLIPCHILDREN
les
SDI (Simple Document Interface)
Les
MDI(Mutiple Document Interface)
Les
boites de dialogues dans lesquelles il faut distinguer
les
modales (bloquant le déroulement du programme principal)
les
modeless (le programme appelant continue )
dans
lesquelles il faut distinguer celles
dont la forme est contenu dans un fichier de ressource et celles
dont la forme est créer en mémoire(masm32 fournit des macros
pour les créer)
A chaque type de fenêtre correspond une boucle de programme avec ces propres fonctions de terminaison et ces propres règles d'écriture et pour les boites de dialogue ces propres règles d'accès a la mémoire ,pas toujours explicite.De plus certains styles et styles étendues ne s'appliquent qu'a un certain type de fenêtres.On peut facilement mélanger les styles ensembles et obtenir des fenêtres bloquées,comme dans l'exemple ci-dessus.
La MDI est dédié aux documents possédant plusieurs fenêtres et semble être la forme la plus adaptable a l'évolution du projet,la création de la feuille de départ est différente de la création des feuilles filles
La SDI n'a au départ qu'une seule fenêtre.Elle peut créer autant de filles qu'elle veut en utilisant toujours la même technique . Les feuilles filles possèdent les mêmes propriétés que la feuille mêre.C'est à dire qu'elle peut remplacer la MDI sans problème.Une feuille fille peut servir aussi de boite de dialogue,il suffit d'utiliser CreateWindowEx
Les boites de dialogues dont la forme est défini dans un fichier de ressource présentent l'inconvénient d'avoir une taille fixe en fonction de la résolution d'écran,mais sont simples a écrire et on peut les rendre modifiables en taille.Celles dont la forme est écrite en mémoire sont assez peu utilisées et restes complexes à écrire malgré les macros fournit par masm32 qu'il faut étudier avant d'en créer une.La mémoire réservée dynamiquement par la mère, ne peut pas etre utilisée par la boite
Ma préférence va a la SDI.Je vous livre içi les deux
choses à savoir pour qu'elles fonctionnent sans problèmes.
Chaque
fille doit avoir son propre nom de classe (et non pas celui de la mère)
déclaré avec la fonction RegisterClassEx,le nom de classe est
uniquement déterminé par votre imagination.
Les feuilles parentes doivent toutes avoir la propriété WS_CLIPCHILDREN si vous ne voulez pas voir vos feuilles filles s'évanouir(problème de peinture).Pourquoi une telle préférence ?
Avec une instruction (createwindowex) et un type de boucle ,on peut créer tout ce qu'on veut
L'exemple ci dessous est conçu pour être reproductible et modifiable.Recopier le texte tel que dans le source et changer toutes les occurences de SDI par le nom de votre choix.Placer déclarations et data aux bons endroits.Vous obtiendrez des fenêtres vides a remplir.
Les styles choisies tiennent compte des nécessités de OLE et évitent les problèmes de peinture.Ne les changer pas sans savoir pourquoi.Les dimensions sont en pourcentage de l'écran ou de la feuille mère.Si Hmere=NULL ,on crée une mère,passer le handle de la mère pour créer une fille.Fonction particulière SystemParametersInfo au lieu de getSystemMetric,permet de tenir compte de la barre des taches plus d'autres choses.La boucle d'attente tient compte des recommandations du sdk (en cas de handle invalide) et diffère des autres asm habituelles qui sont toutes ....inprécises.On peut mettre la boucle d'attente directement après le ou les CreerSDI ce qui suprime winmain,parfaitement inutile.Après la boucle exitprocess et c'est tout.
Si au premier essai la fille est invisible,vous avez oublié de modifier le texte de la classe fille
Remplacer SDI par un nom générique , en utilisant le menu d'édition,a partir d'ici
;___________________________________________________________________________________________________________
;declare
CreerSDI PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
PourCent PROTO :DWORD, :DWORD
;data
msg MSG <>
HwndSDI dd 0
Hinstance dd 0
;code
mov Hinstance,FUNC(GetModuleHandle, NULL) ;FUNC retourne eax
invoke CreerSDI,Hinstance,NULL,0,0,75,75
;################################################################
CreerSDI PROC ModHandle:DWORD, Hmere:DWORD, Xsdi:DWORD, Ysdi:DWORD, largeurSdi:DWORD, HauteurSdi:DWORD
LOCAL wc:WNDCLASSEX
Local ecran:DWORD
Local hauteur:DWORD
Local largeur:DWORD
Local rect:RECT
Local Xpos:DWORD
Local Ypos:DWORD
Local Hcreateicon:DWORD
Local retour:DWORD
;Local :DWORD
;Local [MAX_PATH +1]:BYTE
mov retour,1
;----------------- vérifiez les paramètres ------------------------
;--- içi mieux vaut appeler la fonction en dehors du proc et placer le handle dans Hinst
.if ModHandle == 0
mov ModHandle,FUNC(GetModuleHandle, NULL) ;FUNC retourne eax
.endif
jmp PardessusTexte
classSDI db "TestFenetre",0 ;doit etre unique pour chaque feuille
WindowNameSDI db "exemple de titre",0 ;changer le c'est mieux
;MAINMENU MENUEX MOVEABLE IMPURE LOADONCALL DISCARDABLE ; dans rc
MenuSDI db "MAINMENU",0
PardessusTexte:
;-------------- remplir la definition de classe ---------------------
mov wc.cbSize,sizeof WNDCLASSEX
mov wc.style,CS_BYTEALIGNCLIENT or \ ;a conserver de préférence
CS_BYTEALIGNWINDOW
PuPo wc.lpfnWndProc, WndSDI ;sera renommé en remplaçant SDI
mov wc.cbClsExtra, NULL ;extra bytes , utilisation très spéciale
mov wc.cbWndExtra, NULL
PuPo wc.hInstance, ModHandle
;500 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "MAINICON.ICO" ; dans le .rc
mov Hcreateicon,FUNC(LoadIcon,ModHandle, 500) ;changer le 500 si autre icon dans .rc
PuPo wc.hIcon, Hcreateicon
PuPo wc.hCursor, FUNC(LoadCursor, NULL, IDC_ARROW) ;pointeur standard
PuPo wc.hbrBackground, COLOR_BTNFACE+1 ;couleur de fond standard
.if Hmere == 0 ;fille pas de menu
mov wc.lpszMenuName, offset MenuSDI ;peut etre NULL
.else
mov wc.lpszMenuName,NULL
.endif
mov wc.lpszClassName, offset classSDI
PuPo wc.hIconSm, Hcreateicon ;petit icon
invoke RegisterClassEx, ADDR wc
;renvoie un atom contenant le nom de classe créée,perdu içi
;----------- dimensionner la fenêtre,les dimensions passés a l'appel sont en % -------------
.if Hmere == NULL ;on crée la feuille mère
invoke SystemParametersInfo,SPI_GETWORKAREA,NULL,addr ecran,NULL
mov hauteur,HAUTEUR (ecran)
mov largeur,LARGEUR (ecran)
invoke PourCent,hauteur,HauteurSdi
mov hauteur,eax
invoke PourCent,largeur,largeurSdi
mov largeur,eax
;------- on centre la feuille
mov edx,HAUTEUR (ecran)
sub edx,hauteur
shr edx,1 ;divise par deux
mov Ypos,edx
mov edx,LARGEUR (ecran)
sub edx,largeur
shr edx,1
mov Xpos,edx
.else ;on crée une fille
invoke GetClientRect,Hmere,addr rect
mov hauteur,HAUTEUR (rect)
mov largeur,LARGEUR (rect)
invoke PourCent,hauteur,HauteurSdi
mov hauteur,eax
invoke PourCent,largeur,largeurSdi
mov largeur,eax
;calcul des bords
.if Xsdi != 0
invoke PourCent,largeur,Xsdi
mov Xpos,eax
.else
mov Xpos,0
.endif
.if Ysdi != 0
invoke PourCent,hauteur,Ysdi
mov Ypos,eax
.else
mov Ypos,0
.endif
.endif
;si la feuille contient plusieurs controles ou feuilles,poursuivre le calcul
;------- creation de la fenetre,
.if Hmere == NULL
;--- WS_CLIPCHILDREN évite que la feuille mère repeigne la fille
;------- voir aussi WS_CLIPSIBLINGS style,flickering
INVOKE CreateWindowEx,WS_EX_LEFT ,ADDR classSDI,ADDR WindowNameSDI,
WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN ,\
Xpos,Ypos,largeur,hauteur, NULL, NULL,ModHandle, NULL
.else
; WS_POPUP incompatible avec WS_CHILD
invoke CreateWindowEx,WS_EX_LEFT , ADDR classSDI,ADDR WindowNameSDI,
WS_CHILD or WS_OVERLAPPEDWINDOW ,
Xpos,Ypos,largeur,hauteur,
Hmere,NULL,ModHandle,NULL
.endif
;handle perdu,initialiser par wndSDI
;---------------- visualiser la fenetre ----------------------- ou ajouter WS_VISIBLE
INVOKE ShowWindow,HwndSDI, SW_SHOWNORMAL
INVOKE UpdateWindow,HwndSDI
FindeCreerSDI:
mov eax,retour
ret
CreerSDI endp
;################################################################
WndSDI proc uses ebx esi edi hwnd:DWORD, uMsg, wParam, lParam
Local hauteur:DWORD,largeur:DWORD,fwSizeType:DWORD
Local rect:RECT
Local Xpos:DWORD,Ypos:DWORD
Local retour:DWORD
PuPo HwndSDI,hwnd ;XP accepte uniquement celui la
.if uMsg == WM_CREATE
mov ebx,lParam
;mov eax, (CREATESTRUCT ptr [ebx]).hwnd
;mov edx,(CREATESTRUCT ptr [ebx]).hMenu
;suite
mov retour,0
.elseif uMsg == WM_COMMAND
HIWORD wParam ;evenements des listbox ,combo sont ici
mov edx,eax ;exemple :TCN_SELCHANGE
LOWORD wParam ;IDentificateurs de commandes eax
mov ebx,lParam ; handle du controle
;suite
mov retour,0
.elseif uMsg == WM_SIZE
mov eax, wParam ;resizing flag
mov fwSizeType,eax
LOWORD lParam ;largeur fenetre
mov largeur,eax
HIWORD lParam ;height of client area
mov hauteur,eax
mov retour,0
.ELSEIF uMsg == WM_CLOSE ;return zero
INVOKE DestroyWindow, hwnd
mov retour,0
.ELSEIF uMsg == WM_DESTROY
INVOKE PostQuitMessage, NULL
mov retour,0
.else
INVOKE DefWindowProc, hwnd, uMsg, wParam, lParam
mov retour,eax
.endif
mov eax,retour
;pushad
;invoke EspionneMessages,hwnd,uMsg,wParam,lParam,sortie
;popad
ret
WndSDI endp
;--------------------- boucle d'attente --------------------------------------------
@@:
INVOKE GetMessage, addr msg, NULL, 0, 0
.if eax != 0 ;le message est WM_QUIT ; quitter sur commande quit
.if eax != -1 ;handle invalid,crash
INVOKE TranslateMessage, addr msg ;prend le message
INVOKE DispatchMessageA , addr msg ;envoi les messages aux fenêtres
.endif
jmp @B
.endif
mov eax, msg.wParam
;################################################################
PourCent proc source:DWORD, percent:DWORD
; invoke PourCent,Xresolution,80 ;80 % de Xresolution
LOCAL var1:DWORD
mov var1, 100 ; to divide by 100
FINIT ; l'instruction qui manque dans lib masm32
fild source ; load source integer
fild var1 ; load 100
fdiv ; divide source by 100
fild percent ; load required percentage
fmul ; multiply 1% by required percentage
fist var1 ; store result in variable
mov eax, var1
ret
PourCent endp
; #####################
Copyright © 2005 Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.