Texel
Win32 | Index | Contact

Les bases de la programmation Windows

CHAPITRE II
Première fenêtre



Lorsque vous écrirez des programmes DirectX ou OpenGL, vous serez obligés de créer une fenêtre même si votre application tourne en plein écran en mode exclusif. Cliquez ici pour ouvrir une fenêtre avec le code source de notre deuxième programme pour afficher une fenêtre et détecter l'appui de la touche "flèche gauche" (téléchargez la source ici ):



I. Afficher une fenêtre

Tout comme votre application, vos fenêtres doivent avoir un handle. Il vous permet de définir dans la quelle de vos fenêtres vous voulez afficher quelque chose. Son type est HWND.

Pour afficher une fenêtre, il faut :

_ Créer une classe de fenêtre qui contient les caractéristiques de celle-ci (Avec ou sans menu, redimensionnable ou non …). C'est une structure de type WNDCLASS qu'il vous faut remplir.

_ Enregistrer la classe avec la fonction RegisterClass. Elle permet d'allouer de la mémoire pour stocker les informations sur la classe de fenêtre.

_ Obtenir un handle pour votre fenêtre et définir de quelle manière elle doit être afficher pour la première fois à l'écran (emplacement à l'écran, taille …) en utilisant CreateWindow.

_ Afficher la fenêtre à l'aide de ShowWindow et UpdateWindow.


1. La classe de fenêtre

Examinons de près quelques champs de la structure :

wndclass.style : Ce champ indique le style de la fenêtre. Dans notre programme il est égal à NULL car nous ne dessinons qu'une fenêtre basic. Comme pour MessageBox, on peut utiliser des constantes et même les combiner en les séparant par " | ". Exemple: CS_HREDRAW et CS_VREDRAW permettent à la fenêtre de remettre à jour le contenu de la zone cliente lorsque l'utilisateur redimentionne la fenêtre. La zone cliente est la zone de la fenêtre qui ne contient ni le titre de la fenêtre ni le cadre. Notre fenêtre n'a pas besoin de ces identificateurs car elle ne contient rien. Mais elle pourra tout de même être redimentionnée.

wndclass.lpfnWndProc : Ce champ sert à choisir la procédure de fenêtre de la classe. Cette procédure traitera tous les messages relatifs à la fenêtre créée à partir de cette classe. Nous parlerons des messages et de cette procédure bientôt.

wndclass.cbClsExtra et wndclass.cbWndExtra : Inutiles pour débuter (allouent de la mémoire supplémentaire pour les fenêtres ).

wndclass.hIcon et wndclass.hCursor : Ils chargent un icône et un curseur de souris pour notre fenêtre. Ces éléments sont chargés par les fonctions LoadIcon et LoadCursor. Ils peuvent être chargés à partir de bibliothèques standards incluses dans Windows ou de ressources personnelles incluses dans vos programmes. Dans notre application, le premier argument de ces fonctions est NULL pour charger des éléments standards. Nous verrons dans un prochain chapitre que cet argument peut être remplacer par l'handle de l'application lors de l'utilisation d'une ressource inclus dans l'exécutable. Les deuxièmes arguments de chacune de ces fonctions sont des constantes représentants un icône et un curseur standard.

wndclass.hbrBackground : Ce champ indique la couleur de fond de la zone cliente. La fonction GetStockObject renvoit l'handle d'un pinceau ( ici de couleur blanc). Un pinceau est un motif destiné à remplir une zone.


2. CreateWindow

Le troisième argument de la fonction CreatWindow est WS_OVERLAPPEDWINDOW. Cette constante indique que notre fenêtre doit avoir un titre (dans une barre bleue) et une bordure. N'hésitez pas à consulter les fichiers d'aides pour connaître les autres constantes.

La position et les largeurs de la fenêtre sont définis comme étant celle par défaut par la constante CW_USEDEFAULT. Vous pouvez remplacer cette constante par des valeurs en pixels.



3. ShowWindow et UpdateWindow

La fonction ShowWindow avec son deuxième paramètre indique de quelle manière afficher la fenêtre au début. Le premier paramètre est l'handle de l'application. Si vous remplacer iCmdShow par SW_SHOWMAXIMIZED elle sera agrandit. SW_SHOWMINNOACTIVE ne l'affichera que sous forme d'icône dans la barre des tâches.

UpdateWindow envoie un message à la procédure de fenêtre pour rafraîchir la zone cliente de la fenêtre.



II. Les messages

Windows étant un OS multitâche, il communique avec les différentes applications et fenêtres par des messages. Un message peut indiquer qu'une touche a été tapée ou qu'il faut redessiner la fenêtre après qu'une autre fenêtre se soit superposer dessus. Une fois que l'on a créé une structure de type MSG qui contiendra les messages, il faut, pour les traiter, créer une boucle de message qui lira les messages que Windows place dans une file d'attente. Cette boucle renverra les messages à une procédure de fenêtre qui va modifier la fenêtre en conséquence.


1. La structure MSG

La structure MSG contient six champs :

_ hwnd: l'handle de la fenêtre destinée à recevoir le message.

_ message : Nombre identifiant le message. Ici, encore, des identificateurs facilitent la gestion des messages pour le programmeur. Ils commencent par le préfixe WM_ (exemple: WM_CREATE est envoyé immédiatement après la création de la fenêtre). Vous trouverez la liste des identificateurs et leur signification dans le fichier d'aide win32.hlp.

_ wParam : Paramètre de message 32 bits utilisé pour certains messages.

_ lParam: Idem

_ time: Permet de connaître à quelle heure le message a été placé dans la file d'attente.

_ pt: Coordonné de la souris lors de l'arriver du message dans la file d'attente.


2. La boucle de message

while (GetMessage (&msg, NULL, 0, 0))
{
	TranslateMessage (&msg) ;		
	DispatchMessage (&msg) ;		
}







Cette boucle contient trois fonctions.

GetMessage lit les messages placés dans la liste d'attente et remplit la structure msg. Si ce message est WM_QUIT, GetMessage retourne 0 pour sortir de la boucle while. Le deuxième paramètre est un handle de fenêtre. Ici, NULL permet de lire les messages de toutes les fenêtres. Les deux derniers paramètres permettent de lire tous les messages. En l'absence de messages, l'application est au repos.

TranslateMessage repasse la structure msg à Windows pour qui fasse un transcodage du clavier. Ce qui permet de traduire les événements de touche en événement de caractère. En faite, cette fonction permet entre autre de gérer la tape de plusieurs touche qui produise un caractère. Exemple: "SHIFT" + "a" donne le caractère "A".

DispatchMessage repasse la structure msg à Windows pour qu'il la renvoi à la procédure de fenêtre.



3. La procédure de fenêtre

Le nom de notre procédure de fenêtre est WndProc. C'est le nom que les programmeurs lui donne en général.

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)



Les quatre arguments de cette procédure sont des champs de la structure MSG.

Pour traiter les messages, on utilise généralement un switch.

Lorsque Windows détruit une fenêtre parce que l'utilisateur lui en à donner l'ordre ( En cliquant par exemple sur la croix de fermeture) il envoi le message WM_DESTROY. PostQuitMessage envoie alors un message WM_QUITE qui nous permet de sortir de la boucle des messages de WinMain. Notez qu'il existe aussi le message WM_CLOSE qui indique que l'utilisateur a donné l'ordre à la fenêtre de se fermer. Contrairement à WM_DESTROY la fenêtre n'a pas encore commencer à être détruite par Windows lorsque le message est émit. Vous pouvez donc traiter ce message pour effectuer des opérations avant la destruction de la fenêtre (sauvegardes, MessageBox …).

Lorsqu'une touche est tapée, le message WM_KEYDOWN est envoyé. La valeur de la touche pressée est située dans le champ wParam de la structure MSG et est appelée code de touche virtuelle. Ses identificateurs commencent par VK_

Pour gérer les messages indispensables que nous n'avons pas traité dans notre procédure de fenêtre, il ne faut pas oublier d'utiliser la fonction DefWindowProc .

Après être sortie de la boucle de message, on retourne la valeur du champ wParam de la structure MSG passée à la fonction PostQuitMessage.




Note:

Dans nos programmes DirectX et OpenGL, nous serons amené à remplacer la fonction GetMessage par PeekMessage. Elle permettra à notre application de travailler en l'absence de message ( au lieu de rester au repos). Le code de la boucle sera de ce type:

While(TRUE){
	If(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
		If(msg.message==WM_QUITE)
			break;
		TranslateMessage (&msg) ;		
         	DispatchMessage (&msg) ;
	}
	else{
		// affichage des images, calcul des positions …
	}
}













La constante PM_REMOVE de PeekMessage permet d'effacer un message de la file d'attente après qu'il est été traité par la fonction.




Version originale: Fevrier 2001
Dernière mise à jour: Juin 2002
Par Grégory Smialek
www.texel.fr.fm