Vous êtes sur la page 1sur 62

Tutorial d'initiation

A la programmation avec l'API Windows


24 novembre 2003
Par Bob et CGi
Chapitre 1 : Les bases d'un programme Windows
1. Cration d'une bote de dialogue
2. Cration d'une fentre
3. Affichage dans une fentre
4. Lecture dans un fichier et bote de dialogue
5. Pas pas pour C++ Builder
6. Lecture d'un fichier WAV
7. Cration d'une application console
8. Fentre classique ou ressource ?
Chapitre 2 : Les botes de dialogue
1. Fonctionnement gnral
2. Initialisation
3. Le contrle 'Edit'
4. Le contrle 'Check Box'
5. Le contrle 'Radio Button'
6. Le contrle 'List Box'
7. Le contrle 'Progress bar'
8. Le contrle 'Combo box'
9. Synthse sur les contrles
10. Deux projets rcapitulatifs
11. MessageBox()
12. Parcourir l'arborescence
Chapitre 3 : Les fentres
1. Fentres et botes de dialogue
2. Fonctionnement gnral d'une fentre
3. Rcupration des messages
4. Cration d'une fentre
5. Destruction d'une fentre
6. Contexte d'affichage
7. Gestion de base d'une fentre
8. Interactions avec l'utilisateur
9. Les timers
10. Un projet rcapitulatif
11. Affichage de texte dans une fentre
12. Utilisation de polices personnalises
13. Affichage d'une image
14. Dessin
15. Fond personnalis
16. Commandes systmes
17. Menus
18. Cration dynamique d'un contrle
Chapitre 4 : Le systme de fichier
Page 1 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
1. Introduction
2. Cration et ouverture d'un fichier
3. Lecture/Ecriture dans un fichier
4. Manipulations sur les fichiers
5. Enumration de fichiers
6. Manipulations sur les dossiers
7. Manipulations des chemins
Chapitre 5 : Le multithreading
1. Introduction
2. Notion de processus et de thread
3. Partage des tches
4. Synchronisation
5. Premier exemple
6. Arrt d'un thread
7. Rcupration des messages
8. Communication inter-threads
9. Sections Critiques
10. Fonctions d'attente
11. Evnements
12. Smaphores
13. Trois projets simples
14. Performances





Chapitre 1
Les bases d'un programme Windows
1. Cration d'une bote de dialogue
Cours thorique :
Bien comprendre le principe de fonctionnement d'une application Windows est essentiel avant
de commencer programmer. Je vais ici exposer le principe global de fonctionnement d'une
application crant une fentre.
Tout d'abord, il convient de bien garder l'esprit qu'un programme fonctionnant sous Windows
et grant une fentre doit rester en dialogue permanent avec Windows. Le programme ne connait (a
priori) rien sur son environnement. C'est Windows qui signale l'application si elle doit redessiner le
contenu d'une de ses fentres, ou encore si l'utilisateur essaie de fermer la fentre.
Cette communication se fait au travers de messages que Windows envoie chaque fentre
concerne. C'est au programme d'effectuer la rception de ces messages et de les transmettre aux
fonctions grant les diffrentes fentres. La rception de ces messages ne doit donc pas prendre de
Page 2 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
retard, et le traitement de chacun des messages doit tre bref. En cas de retard, le
redessinement des fentres n'est donc plus assur, ce qui a pour consquence des fentres blanches,
indplaables, similaires celles des programmes "plants".
Chaque fentre est associe une fonction ou procdure de fentre (Window Proc). Parfois
plusieurs fentres peuvent tre associes une mme procdure. Chaque message reu est transmis
la procdure correspondante qui se chargera de traiter ce message (redessiner la fentre, la
redimensionner, afficher un caractre entr par l'utilisateur...).
Une partie du travail de rafraichissement de la fentre est pris en charge par Windows. Le
programme n'a redessiner que la zone client de sa fentre (et non pas la barre de titre, les menus
ventuels...).
Projet N1 :
- Objectif : crer une bote de dialogue et une procdure trs simple charge de la grer.
- Ralisation : la bote de dialogue sera cre grce l'diteur de ressources de VC++. Le
programme sera charg de la rception des messages et du traitement des plus simples.
Tlcharger le projet ici.
- Le projet pas pas :
Tout d'abord, il s'agit de crer le projet dans VC++ : File/New/Projects/Win32 Application.
Donnez ensuite un nom votre projet et cliquez 'Ok'. Ensuite, slectionnez 'An empty project' puis
'Finish' et 'Ok'. Nous venons de crer un projet vide, il ne contient aucun fichier de code.
Ajoutons maintenant un fichier de code et fichier de ressources. Allez dans
File/New/Files/C++ Source File. Appelez ce fichier main.cpp et validez. Refaites la mme
opration mais avec un fichier de type Ressource Script nomm res. Fermez le fichier qui s'est
ouvert dans l'diteur. Le script de ressources est associ un fichier d'en-tte cr et mis jour
automatiquement, mais qu'il convient d'ajouter. Dans le feuillet FileView du panneau de gauche,
faites un clic droit sur Header Files et slectionnez Add Files to Folder. Ajoutez le fichier
'ressource.h' qui doit tre dans le mme rpertoire que votre projet.
Crons prsent le modle de notre bote de dialogue. Allez dans le feuillet RessourceView
du panneau de travail ( gauche). Faites un clic droit sur res ressources et slectionnez
Insert/Dialog/New. Nous ne modifirons pas cette bote de dialogue.
Occupons-nous maintenant du code qui va constituer le programme. Tout d'abord, les fichiers
d'en-tte, deux sont ncessaires, Windows.h et ressource.h que nous avons inclus prcdemment.
Le point d'entre du programme est la fonction WinMain(). Elle est excute automatiquement
par Windows lorsque le programme est dmarr. Mais avant cette fonction, nous aurons la dfinition
de la procdure de notre fentre principale, MainPoc().
#include <Windows.h>
#include "ressource.h"

Page 3 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php

LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
Nous allons maintenant crer la bote de dialogue grce la fonction CreateDialog(). Le
paramtre HINSTANCE est un identifiant de notre application, on passe l'identifiant fourni par
Windows en paramtre de la fonction WinMain(). On donne ensuite l'identifiant ressource de notre
bote de dialogue. Elle n'a pas de fentre parent et la procdure est la fonction MainProc(). La
fonction retourne une variable de type HWND qui identifie notre fentre. Puis, on affiche cette
fentre. Toute fentre cre est invisible par dfaut.
HWND hDlg;
hDlg=CreateDialog(hInstance,(LPCTSTR)IDD_DIALOG1,NULL,(DLGPROC)MainProc);
ShowWindow(hDlg,SW_SHOW);
Occupons-nous maintenant de la rception des messages. La rception des messages est prise
en charge par la fonction GetMessage(). Cette fonction retourne FALSE ds que le message
WM_QUIT est reu, ce qui indique que l'application doit tre ferme.
MSG msg;
while(GetMessage(&msg,NULL,0,0)==TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Il ne reste plus qu' dfinir le comportement de notre fentre dans quelques cas simples: elle
doit tre ferme et l'application quitte si l'utilisateur presse 'ok' ou essaie de quitter. La fermeture de
la fentre ne signifie pas forcment l'arrt de l'application. Ces deux comportements correspondent
deux messages distincs: WM_QUIT indiquie que l'application doit se terminer, WM_CLOSE
indique la fermeture de la fentre. Mais dans ce cas, on a une fentre un peu particulire, puisqu'il
s'agit d'une bote de dialogue. Sa procdure est dfinie de la mme manire que celle d'une fentre.
LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam)
{
int Select;
switch(message)
{
case WM_COMMAND:
Select=LOWORD(wParam);
switch(Select)
{
case IDOK:
EndDialog(Dlg,0);
PostQuitMessage(0);
return TRUE;
case IDCANCEL:
EndDialog(Dlg,Select);
PostQuitMessage(0);
Page 4 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
return TRUE;
}
default:
return FALSE;
}
}
Cette procdure est trs simple, elle ne ragit qu' la pression des boutons 'Ok' et 'Cancel' (le
bouton ou la "croix"). En rponse l'un ou l'autre de ces vnements, la procdure demande la
fermeture de la bote de dialogue et termine l'application en envoyant un message WM_QUIT grce
la fonction PostQuitMessage().
2. Cration d'une fentre
Cours thorique :
Dans la premire partie de ce chapitre, nous avons vu comment crer une boite de dialogue. La
cration d'une boite de dialogue grce la fonction CreateDialog() est relativement simple
puisqu'une grande partie du travail est effectu par CreateDialog().
Nous allons donc voir maintenant comment crer une fentre. Cette fentre pourra tre
personnalise et ne rferrera aucune resource. Notre programme n'utilisera donc pas de resources.
Comme nous avons pu le voir dans la partie prcdente, le programme n'intervient en aucun
cas dans le dessin de la fentre (barre de titre, menu...). Le programme n'est responsable que du
redessinement de la zone client de la fentre. Comme il existe plusieurs types de fentre (avec un
menu, sans menu, pouvant tre minimise ou pas...) il est ncessaire que de crer un 'protoype de
fentre'. Ce prototype est aussi appel classe de la fentre. C'est sur ce prototype que Windows se
basera pour crer la fentre.
Une fois le prototype de fentre cr, nous demanderons Windows de crer une fentre
suivant ce prototype. La fentre cre devra elle aussi disposer d'une procdure permettant de traiter
les messages qu'elle reoit. Cette procdure est trs similaire celle grant la boite de dialogue de la
partie prcdante. Elle ne traite cependant pas exactement les messages de la mme manire.
Projet N2 :
- Objectif : crer une fentre sans utiliser de resource et une procdure permettant sa gestion.
- Ralisation : la fentre sera cre sans utiliser de resource et sa procdure traitera les
messages les plus simples.
Tlcharger le projet ici.
- Le projet pas pas :
Il s'agit tout d'abord de crer un projet dans Microsoft Visual C++. Le projet sera cr de la
mme manire que dans la partie prcdente, mais sans y ajouter de Ressource Script.
Page 5 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Le point d'entre WinMain() est un standard toute application Windows, ce programme
dbutera donc de la mme manire que le prcdant.
#include <Windows.h>


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
Il faut mintenant crer une nouvelle classe de fentre. Cette classe est cre au moyen de la
fonction RegisterClassEx(). La cration de cette classe paut paratre fastidieuse et comprend un
grand nombre de paramtres. nous n'utiliserons pas tous les paramtres, donc ne vous effrayez pas si
vous ne comprenez pas totalement l'utilit de chaqun d'eux.
WNDCLASSEX principale;
principale.cbSize=sizeof(WNDCLASSEX);
principale.style=CS_HREDRAW|CS_VREDRAW;
principale.lpfnWndProc=MainProc;
principale.cbClsExtra=0;
principale.cbWndExtra=0;
principale.hInstance=hInstance;
principale.hIcon=LoadIcon(NULL,IDI_APPLICATION);
principale.hCursor=LoadCursor(NULL,IDC_ARROW);
principale.hbrBackground=reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
principale.lpszMenuName=NULL;
principale.lpszClassName="std";
principale.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&principale);
principale.style=CS_HREDRAW|CS_VREDRAW indique que notre fentre devra tre
redessine en cas de redimentionnement horizontal ou vertical. Nous indiquons ensuite quelle
procdure sera charge de grer la fentre. L'instance de notre application est elle aussi passe en
paramtre. Les curseurs ou icones de notre fentre sont ceux par dfaut de Windows. La fentre n'as
pas de menu, la couleur de fond est celle de Windows par dfaut. Le nom de la classe de la fentre
'std' est un choix personnel. Il n'a d'importance que pour nous. Une fois la structure remplie, un appel
RegisterClassEx() demande Windows de mmoriser la classe.
Maintenant que nous disposons de notre classe de fentre, il ne reste plus qu'a la crer grce
la fonction CreateWindowEx().
HWND hWnd;
hWnd=CreateWindowEx(
WS_EX_CLIENTEDGE,
"std",
"Notre fentre",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
Page 6 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
NULL,
hInstance,
NULL
);

ShowWindow(hWnd,SW_SHOW);
La rception des messages se fait exactement de la mme manire que dans le projet prcdent.
MSG msg;
while(GetMessage(&msg,NULL,0,0)==TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
La dfinition de la procdure de cette fentre est la mme que dans le programme prcdent.
Mais son fonctionnement interne est diffrent.
Tout d'abord, cette fentre ne contient pas de bouton. Sa fermeture se fera lors d'un clic sur la
'croix'. Les messages non traits sont passs DefWindowProc(). Les messages traits seront
WM_PAINT et WM_DESTROY. WM_PAINT indique que nous devons redessiner la zone client.
Ici le traitement de ce message ne fera rien et pourrait tre ignor. Il est inclu dans le seul but de
comprendre le mcanisme de redessinement d'une fentre. Le message WM_DESTROY indique que
la fentre est en train d'tre dtruite (probablement suite au clic sur la 'croix'). Le fermeture de la
fentre ne signifie pas forcment l'arret de l'application. Mais comme dans ce cas notre application
n'est forme que d'une fentre, nous demanderons terminer l'application dans ce cas en postant un
message WM_QUIT.
LRESULT CALLBACK MainProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT paintst;
switch (mes)
{
case WM_PAINT:
hDC=BeginPaint(hWnd,&paintst);
EndPaint(hWnd,&paintst);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, mes, wParam, lParam);
}
}
Le traitement du message WM_PAINT peut vous paraitre trange. En effet, pourquoi effectuer
un BeginPaint() et un EndPaint() alors que nous ne dessinons rien. La raison est trs simple. A partir
du moment ou nous dcidons de traiter ce message, nous devons assurer un traitement minimum par
la suite de ces deux fonction, mme si rien n'est dessin.
3. Affichage dans une fentre
Page 7 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Maintenant que nous savons crer une fentre, nous allons voir comment dessiner dans sa zone
client. L'affichage dans la zone client d'une fentre se fait au niveau du message WM_PAINT. Pour
provoquer l'envoi d'un tel message, il faut demander le redessinement d'une zone de la fentre. Lors
du dessin dans la partie WM_PAINT, il sera impossible d'afficher hors de la zone de redessinement.
Pour dessiner dans une fentre, nous allons utiliser un contexte d'affichage. Il est propre la
fentre dans laquelle nous dessinons et nous est fourni par Windows. Il identifie en fait la zone de
l'cran dans laquelle nous allons dessiner.
Projet N3 :
- Objectif : crer une fentre et afficher l'heure.
- Ralisation : la cration de la fentre se fera de manire identique au projet prcdent. Seule
sera ajoute la partie permettant l'affichage de l'heure courante.
Tlcharger le projet ici.
- Le projet pas pas :
La cration de la fentre se fait exactement de la mme manire que dans le projet prcdent.
Nous allons simplement utiliser les fonctions contenues dans stdio.h.
#include <Windows.h>
#include <stdio.h>


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX principale;
principale.cbSize=sizeof(WNDCLASSEX);
principale.style=CS_HREDRAW|CS_VREDRAW;
principale.lpfnWndProc=MainProc;
principale.cbClsExtra=0;
principale.cbWndExtra=0;
principale.hInstance=hInstance;
principale.hIcon=LoadIcon(NULL,IDI_APPLICATION);
principale.hCursor=LoadCursor(NULL,IDC_ARROW);
principale.hbrBackground=reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
principale.lpszMenuName=NULL;
principale.lpszClassName="std";
principale.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
RegisterClassEx(&principale);

HWND hWnd;
hWnd=CreateWindowEx(
WS_EX_CLIENTEDGE,
Page 8 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
"std",
"Notre fentre",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd,SW_SHOW);
Comme nous allons afficher l'heure, il est important que nous soyons prvenus de manire
rgulire. Nous allons demander Windows de nous avertir toutes les secondes, de manire
afficher l'heure rgulirement. Notre fentre recevra un message WM_TIMER toutes les secondes.
SetTimer(hWnd,NULL,1000,NULL);
La rception des messages se fait exactement de la mme manire que dans le projet prcdent.
MSG msg;
while(GetMessage(&msg,NULL,0,0)==TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
La procdure fonctionne de manire identique que prcdemment. Nous allons ajouter le
traitement du message WM_TIMER qui sera recu toutes les secondes. Ce message demandera le
redessinement d'une zone de l'cran (la zone ou nous afficherons l'heure).
LRESULT CALLBACK MainProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT paintst;
RECT rcClient;
switch (mes)
{
case WM_TIMER:
rcClient.top=0;
rcClient.left=0;
rcClient.right=100;
rcClient.bottom=50;
RedrawWindow(hWnd,&rcClient,NULL,RDW_ERASE|RDW_INVALIDATE|RDW_ERASE
NOW|RDW_NOCHILDREN);
return 0;
Un message WM_PAINT sera donc reu toutes les secondes et chaque fois que la fentre
doit tre redessine. La fentre doit tre redessine si elle est couverte puis dcouverte par une autre
fentre (par exemple).
Nous allons afficher l'heure en utilisant une police spcifique. Une fois cette police cre, il est
ncessaire d'indiquer que nous allons l'utiliser dans notre contexte d'affichage (HDC). Le contexte
d'affichage (ou Device Context) rfre la zone client de notre fentre et contient tous ses
paramtres graphiques (disposition sur l'cran, nombre de couleurs affichables...). Il est identifi par
Page 9 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
la variable hDC qui nous est passe par Windows au travers de la fonction BeginPaint().
Une fois la police cre et le texte dessin, il convient de dtruire la police.
L'heure courante sera rcupre grace la fonction GetLocalTime().
case WM_PAINT:
char buf[256];
SYSTEMTIME CurrentTime;
HFONT hFont;
hFont=CreateFont
(20,0,0,0,700,FALSE,FALSE,FALSE,0,OUT_DEFAULT_PRECIS,CLIP_
DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Comic Sans MS");
hDC=BeginPaint(hWnd,&paintst);
SelectObject(hDC,hFont);
GetLocalTime(&CurrentTime);
sprintf(buf,"%d : %d : %
d",CurrentTime.wHour,CurrentTime.wMinute,CurrentTi
me.wSecond);
TextOut(hDC,0,0,buf,strlen(buf));
EndPaint(hWnd,&paintst);
DeleteObject(hFont);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, mes, wParam, lParam);
}
}
Il est essentiel de bien comprendre le principe d'affichage dans une fentre car il est trs utilis
par Windows. Quelque soit le cas, l'affichage se fait toujours entre deux fonctions qui indiquent le
dbut et la fin de l'affichage. Il ne faut jamais oublier de librer le contexte d'affichage aprs l'avoir
utilis. Ici, la libration du contexte d'affichage se fait par la fonction EndPaint().
4. Lecture dans un fichier et bote de dialogue
Cours thorique :
Nous allons maintenant revenir sur un programme utilisant des ressources. En effet,
l'utilisation de boites de dialogues cres en ressources permettent bien souvent de gagner un temps
prcieux pour des programmes de petites tailles et de faible complexit. C'est le cas du programme
que nous allons raliser ici.
Nous allons donc aborder deux thmes principaux: les boites de dialogues et les controles
prdfinis, ainsi que la lecture d'un fichier par des fonctions de l'API Windows, donc sans utiliser les
fonctions du C traditionnel. Les controles prdfinis sont les 'edit boxes', 'combo boxes', 'buttons',
etc. Leur utilisation est trs simple car ils sont grs de manire entirement autonome. Ils envoient
la fentre qui les a crs des messages de notification pour indiquer les actions de l'utilisateur: clic
sur le boutton, saisie d'un caractre...
La lecture du fichier se fait de manire relativement simple, et fonctionne par un systeme de
Page 10 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
HANDLE (comme beaucoup de fonctions de l'API Windows) qu'il faut crer puis dtruire.
Projet N4 :
- Objectif : crer une boite de dialogue permettant la saisie d'un fichier et affichant les 500
premiers octets de ce fichier.
- Ralisation : nous allons ici utiliser une ressource pour crer la boite de dialogue. La lecture
du fichier se fera au niveau de la procdure de la boite de dialogue car elle est trs brve. Nous
utiliserons ici une fonction qui permet de crer la boite de dialogue et de relever les messages, la
partie 'main' du programme sera donc trs courte (2 lignes).
Tlcharger le projet ici.
- Le projet pas pas :
Nous allons crer un programme avec un fichier de ressources, inclure les fichiers
correspondants (voir parties prcdentes). La fonction 'WinMain()' est excessivement simple puisque
la fonction DialogBox() s'occupe de crer la bote de dialogue et de rcuprer ses messages. Cette
fonction retourne seulement une fois que la boite de dialogue est ferme.
#include <Windows.h>


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(hInstance,(LPCTSTR)IDD_MAIN,NULL,(DLGPROC)MainProc);
return 0;
}
La procdure de la bote de dialogue est trs simple, elle ne gre que trois messages provenant
de trois bouttons. IDOK est envoy lors d'un clic sur le boutton OK, IDCANCEL est envoy lors
d'un clic sur la 'croix' et IDC_LIRE est un message envoy lors d'un clic sur le boutton IDC_LIRE
que nous avons cr. Les variables cres en dbut de procdure serviront la lecture du fichier que
nous verrons aprs.
LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam)
{
int Select;
char buf[501];
HANDLE hFile;
DWORD Read;
switch(message)
{
case WM_COMMAND:
Select=LOWORD(wParam);
switch(Select)
{
case IDC_LIRE:
Page 11 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
La relle nouveaut de ce programme va se faire ici. Il s'agit tout d'abord de rcuprer le nom
de fichier entr par l'utilisateur. Comme il est entr dans un contrle prdfini, nous allons utiliser la
fonction GetDlgItemText() et nous plaerons ce nom de fichier dans le buffer cr au debut de cette
procdure sous le nom de 'buf'. Une fois ce nom de fichier rcupr, nous allons essayer d'ouvrir le
fichier donn en lecture. Si nous n'y parvenons pas, un message d'erreur sera affich grce la
fonction 'MessageBox()'. Le traitement du message sera alors arrt.
Remarque: la fonction MessageBox() ne retourne qu'une fois que la bote de dialogue cre est
ferme. Mais la rcupration des messages de notre bote de dialogue est assure par MessageBox().
Le traitement prolong de ce message n'est donc pas gnant dans ce cas puisque les messages
continueront d'arriver notre procdure.
GetDlgItemText(Dlg,IDC_FILENAME,buf,256);
hFile=CreateFile
(buf,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FI
LE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
MessageBox
(Dlg,"Erreur, impossible d'ouvrir le fichier spcifi.","E
rreur",MB_OK);
return 0;
}
Arrivs ce stade nous savons que le fichier saisi est valide et qu'il a pu tre ouvert. Il ne reste
plus qu' lire les 500 premiers octets. Ils seront placs dans le buffer. Comme cette chane vas tre
afiche, elle doit tre termine par un caractre NULL. C'est pour cette raison que 'buf' a t dfini
comme un tableau de 501 caractres (500 pour la lecture et 1 pour le caractre NULL). Comme la
lecture peut faire moins de 500 octets, nous nous baserons sur la valeur retourne par ReadFile()
pour ajouter le caractre NULL. Puis nous afficherons ce buffer dans l'edit box cr a cet effet grace
SetDlgItemText().
ReadFile(hFile,buf,500,&Read,NULL);
CloseHandle(hFile);
buf[Read]='';
SetDlgItemText(Dlg,IDC_TEXT,buf);
return 0;
Le reste du traitement de la procdure ne prsente pas de difficult particulire. Il est similaire
aux traitements prcdents. La seule diffrence notalbe est lors de la fermeture de la bote de
dialogue. Comme elle a t cre avec la fonction DialogBox() il ne faut pas utiliser PostQuitMesage
() pour la quitter mais EndDialog().
case IDOK:
EndDialog(Dlg,0);
return TRUE;
case IDCANCEL:
EndDialog(Dlg,0);
return TRUE;
}
default:
return FALSE;
}
}
Page 12 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Ce programme est relativement simple. La gestion de l'interface graphique ne prend que
quelques lignes. Nous voyons donc bien l'intrt de l'utilisation des ressources et donc contrles
prdfinis. Sans l'aide des ressources, ce programme aurait t beaucoup plus compliqu raliser.
5. Pas pas pour C++ Builder
Nous allons reprendre la 4me partie : "Lecture dans un fichier et bote de dialogue." mais
adapte C++ Builder. Deux procdures sont traites dans ce document une pour BCB6 et une pour
BCB4.
Pour les projets sans bote de dialogue la procdure est la mme sauf qu'il n'y a pas besoin de
rajouter de fichiers ressources.
BCB 6
Faire "Nouveau". (appelle la bote de dialogue Nouveaux lments)
Sur la bote de dialogue "Expert console" :
Sur "Type de source" slectionner "C" ou "C++". (Pour l'exemple cela n'a pas d'importance)
Aucune des CheckBox droite ne doit tre coche.
Clicker sur le bouton "Ok".
Maintenant nous avons le code minimum pour ce type d'application:
#include <windows.h>
#pragma hdrstop


//---------------------------------------------------------------------------
#pragma argsused
WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int
nCmdShow)
{
return 0;}
Enregister le projet sous le nom choisi. Ici "main.cpp" et "main.bpr". (Faire "Fichier" puis
"Tout enregistrer")
J'ai utilis "Resource WorkShop" (livr avec BCB) pour faire le fichier Dialog1.rc mais il peut
se faire avec n'importe quel diteur de ressource.
Puis il faut ajouter le fichier ressource au projet:
Sur le menu faire "Projet" puis "Ajouter au projet"
Selectionner le type de fichier voulu ici "Fichier ressource (*.rc)"
Puis slectionner le fichier ici "Dialog1.rc"
Puis faire Ouvrir
Le code source est identique a l'exemple Visual C++ (J'ai mis les mmes noms d'identificateur)
Page 13 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Les source des fichiers "Dialog1.rc" et "ressource.h" sont a la fin de ce document.
Fichier "main.cpp":
#include <windows.h>
#pragma hdrstop //Pas obligatoire
#include "ressource.h"


//---------------------------------------------------------------------------




LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


#pragma argsused //ajout automatiquement mais pas ncssaire.


WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, in
t nCmdShow)


{
DialogBox(hInstance,(LPCTSTR)DIALOG_1,NULL,(DLGPROC)MainProc);
return 0;
}
//---------------------------------------------------------------------------


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam)
{
int Select;
char buf[501];
HANDLE hFile;
DWORD Read;
switch(message)
{
case WM_COMMAND:
Select=LOWORD(wParam);
switch(Select)
{
case IDC_LIRE:
GetDlgItemText(Dlg,IDC_FILENAME,buf,256);
hFile=CreateFile
(buf,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FI
LE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
MessageBox
(Dlg,"Erreur, impossible d'ouvrir le fichier spcifi.","E
rreur",MB_OK);
return 0;
}
ReadFile(hFile,buf,500,&Read,NULL);
CloseHandle(hFile);
buf[Read]='';
SetDlgItemText(Dlg,IDC_TEXT,buf);
return 0;



Page 14 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
case IDOK:
EndDialog(Dlg,0);
return TRUE;
case IDCANCEL:
EndDialog(Dlg,0);
return TRUE;
}
default:
return FALSE;
}
}
BCB 4
Faire "Nouveau". (appelle la bote de dialogue Nouveaux lments)
Sur la bote de dialogue "Nouveaux lments" Onglet "Nouveau" DbClk sur l'icne "Expert
console". (appelle la bote de dialogue "Expert d'application console")
Sur la bote de dialogue "Expert d'application console" :
Sur "Type de fentre" slectionner "Windows (GUI)".
Sur "Type d'excution" slectionner "EXE".
Clicker sur le bouton "Terminer".
Maintenant nous avons le code minimum pour ce type d'application:
#include <windows.h>
#pragma hdrstop
#include <condefs.h>


//---------------------------------------------------------------------------
#pragma argsused
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
return 0;
}
Enregister le projet sous le nom choisi. Ici "main.cpp". (Faire "Fichier" puis "Tout enregistrer")
J'ai utilis "Resource WorkShop" (livr avec BCB) pour faire le fichier Dialog1.rc mais il peut
se faire avec n'importe quel diteur de ressource.
Puis il faut ajouter le fichier ressource au projet:
Sur le menu faire "Projet" puis "Ajouter au projet"
Selectionner le type de fichier voulu ici "Fichier ressource (*.rc)"
Puis slectionner le fichier ici "Dialog1.rc"
Puis faire Ouvrir
Cela va ajouter cette ligne dans le code :
USERC("Dialog1.rc");
Le reste du code est identique a l'exemple Visual C++ (J'ai mis les mmes noms
Page 15 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
d'identificateur)
Fichier "main.cpp":
#include <windows.h>
#pragma hdrstop
#include <condefs.h>
#include "ressource.h"


//---------------------------------------------------------------------------
USERC("Dialog1.rc");
//---------------------------------------------------------------------------


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM );


#pragma argsused //ajout automatiquement mais pas ncssaire.
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
DialogBox(hInstance,(LPCTSTR)DIALOG_1,NULL,(DLGPROC)MainProc);
return 0;
}


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM )
{
int Select;
char buf[501];
HANDLE hFile;
DWORD Read;
switch(message)
{
case WM_COMMAND:
Select=LOWORD(wParam);
switch(Select)
{
case IDC_LIRE:
GetDlgItemText(Dlg,IDC_FILENAME,buf,256);
hFile=CreateFile
(buf,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FI
LE_ATTRIBUTE_NORMAL,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
MessageBox
(Dlg,"Erreur, impossible d'ouvrir le fichier spcifi.","E
rreur",MB_OK);
return 0;
}
ReadFile(hFile,buf,500,&Read,NULL);
CloseHandle(hFile);
buf[Read]='';
SetDlgItemText(Dlg,IDC_TEXT,buf);
return 0;


case IDOK:
EndDialog(Dlg,0);
return TRUE;
case IDCANCEL:
EndDialog(Dlg,0);
return TRUE;
}
Page 16 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
default:
return FALSE;
}
}
Autres fichiers
Identique pour BCB4 et BCB6
Fichier "Dialog1.rc"
#include "ressource.h"

DIALOG_1 DIALOG 14, 30, 212, 227
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "DIALOG_1"
FONT 8, "MS Sans Serif"
{
CONTROL "Ok", IDOK, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTO

P, 56, 193, 36, 14
CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | W

S_TABSTOP, 115, 193, 38, 14
CONTROL "", IDC_FILENAME, "EDIT", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER |

WS_TABSTOP, 26, 18, 91, 12
CONTROL "", IDC_TEXT, "EDIT", ES_LEFT | ES_MULTILINE | WS_CHILD | WS_VISIBLE |

WS_BORDER | WS_TABSTOP, 18, 49, 175, 119
CONTROL "Lire", IDC_LIRE, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_

TABSTOP, 136, 18, 50, 14
}
Fichier "Ressource.h"
#define DIALOG_1 1


#define IDC_FILENAME 101
#define IDC_TEXT 102
#define IDC_LIRE 103
6. Lecture d'un fichier WAV
Cours thorique :
A prsent, nous allons raliser un nouveau projet qui sera dj un programme 'utile' en soi.
Evidemment, il restera trs sommaire et se contentera de lire un fichier WAV slectionn par
l'utilisateur.
Nous inviterons tout d'abord l'utilisateur choisir le fichier qu'il veut lire partir d'une bote de
dialogue 'parcourir'. Une fois que l'utilisateur aura choisi le fichier qu'il dsire lire, il pourra choisir
de l'couter. La lecture du fichier WAV se fera grce une fonction multimdia fournie par
Windows: sndPlaySound(). Cette fonction est entirement autonome et lira le fichier en arrire plan
sans la moindre intervention de la part du programme. Cet automatisme est trs pratique puisqu'il
permet de lire le fichier sans se soucier de rien. Cependant, les possibilits de cette fonction restent
Page 17 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
restreintes. On pourra se satisfaire de cette fonction dans quasiment toutes les applications
multimdias.
Projet N5 :
- Objectif : crer une boite de dialogue permettant la saisie d'un fichier WAV puis sa lecture.
- Ralisation : nous allons utiliser un fichier ressources qui sera ici trs adapt. Le programme
ne contiendra que la fonction WinMain() et une procdure pour la boite de dialogue.
Tlcharger le projet ici.
- Le projet pas pas :
Comme dans le programme prcdent, nous allons crer un projet d'application Win32, ainsi
qu'un fichier de ressources. La fonction WinMain() contiendra simplement l'appel de notre bote de
dialogue. Remarquons tout de mme que la fonction sndPlaySound() que nous allons utiliser se
trouve dans la librairie 'winmm.lib' qui n'est pas une librairie standard. Il faut donc ajouter cette
librairie. Ceci peut tre fait grce au menu Project/Setting/Link/Input. Dans le champ contenant dj
les librairies par dfaut, ajoutez 'winmm.lib'. Sans cet ajout, nous obtiendrions une erreur la
compilation indiquant que la fonction ne peut pas tre trouve.
La bote de dialogue contiendra un champ 'Edit Box' (IDC_NOMDEFICHIER)dans lequel sera
affich le nom du fichier lire. Nous cocherons la case 'Read Only' dans les options de ce champ de
manire empcher l'utilisateur de saisir lui mme un nom de fichier. De cette manire, la possibilit
de choix errons se trouve dj limite.
Nous ajouterons un bouton 'Parcourir' avec pour identifiant 'IDC_PARCOURIR' et un bouton
'Lire' (IDC_LIRE). Pour quitter, l'utilisateur devra utiliser la 'croix' (ID_CANCEL).
La fonction WinMain() ne diffre pas par rapport au programme prcdent (si ce n'est dans les
enttes inclure). Nous ajouterons une variable globale de manire pouvoir accder l'instance du
programme dans toutes les fonctions du programme (et ici dans la procdure de la bote de dialogue).
#include <Windows.h>
#include <mmsystem.h>
#include "ressource.h"


HINSTANCE hInst;


LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam);


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
hInst=hInstance;
DialogBox(hInstance,(LPCTSTR)IDD_MAIN,NULL,(DLGPROC)MainProc);
return 0;
Page 18 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
}
La procdure de la bote de dialogue n'est pas trs complique. Il faudra toutefois y ajouter les
boutons 'Lire' et 'Parcourir'. Le bouton parcourir sera le plus difficile ajouter.
Nous allons rserver un buffer de taille MAX_PATH (qui est une constante marquant la taille
maximale d'un nom de fichier), ainsi qu'une structure OPENFILENAME qui nous servira pour la
bote de dialogue 'Parcourir'.
LRESULT CALLBACK MainProc(HWND Dlg,UINT message,WPARAM wParam,LPARAM lParam)
{
int Select;
char buf[MAX_PATH];
OPENFILENAME DlgInfs;

switch(message)
{
case WM_COMMAND:
Select=LOWORD(wParam);
switch(Select)
{
case IDC_PARCOURIR:
La fonction qui affiche la bote de dialogue 'Parcourir' est GetOpenfileName(). De manire
personnaliser cette bote de dialogue nos besoins, nous passons une structure contenant les
caractristiques de la bote de dialogue. Comme nous n'utiliserons pas une grande partie des
membres de cette structure, nous allons tous les mettre 0 grce la fonction memset() du C
standard. Ceci constitue une bonne habitude prendre. Pour plus d'informations sur les paramtres et
les possibilits de cette fonction rfrez vous l'aide de fournie par Microsoft.
memset(&DlgInfs,0,sizeof(OPENFILENAME));

DlgInfs.lStructSize=sizeof(OPENFILENAME);
DlgInfs.hwndOwner=Dlg;
DlgInfs.hInstance=hInst;
DlgInfs.lpstrFilter="Fichiers WAV*.WAV";
DlgInfs.lpstrFile=buf;
DlgInfs.nMaxFile=MAX_PATH;
DlgInfs.lpstrTitle="Choisissez le fichier lire.";
DlgInfs.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
if(GetOpenFileName(&DlgInfs))
SetDlgItemText(Dlg,IDC_NOMDEFICHIER,buf);
return TRUE;
Lorsque l'utilisateur pressera le bouton 'Lire', il faudra rcuprer le nom de fichier saisi dans le
champ 'IDC_NOMDEFICHIER' et le lire grce la fonction sndPlaySound().
case IDC_LIRE:
GetDlgItemText(Dlg,IDC_NOMDEFICHIER,buf,MAX_PATH);

sndPlaySound(buf,SND_ASYNC);
return TRUE;
Il ne reste plus qu' grer la sortie de la bote de dialogue. Dans le cas ou l'utilisateur quitte le
programme, il faut stopper la lecture du son en cours. Pour cela on utilisera encore la fonction
sndPlaySound() avant de quitter.
case IDCANCEL:
Page 19 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
sndPlaySound(NULL,NULL);
EndDialog(Dlg,0);
return TRUE;
}
default:
return FALSE;
}
}
Ce programme ne prsente aucune difficult particulire. Il se suffit lui mme bien qu'tant
particulirement pauvre. Ceci permet de donner une ide de la gestion des botes de dialogues qui
seront dans le futur intgres un programme bien plus important. Bien que la gestion de telles
botes de dialogue constitue un dtail dans un programme plus important, il est ncessaire de bien
comprendre leur fonctionnement de manire ne pas perdre de temps et pouvoir crer rapidement
une interface graphique agrable.
7. Cration d'une application console
Cours thorique :
Avant de s'intresser aux applications consoles, il convient de bien comprendre de quoi il
s'agit. Une application console n'est pas un programme DOS. C'est un programme Windows utilisant
les fonctions de l'API Windows mais qui s'affiche dans une console DOS au lieu d'une fentre
classique. Un tel programme ne peut pas tre dmarr en mode DOS rel.
L'avantage de ces programmes est qu'il est inutile de se proccuper de l'affichage de la fentre
puisque toutes les interventions sur la fentre sont prises en compte par le systme. Ce type
d'application est trs utile pour de petits utilitaires fonctionnant en ligne de commande (bien qu'ils
paraissent austres!).
Cependant, ces programmes fonctionnent de la mme manire que les applications DOS (pour
ce qui est de l'entre sortie sur l'cran). On pourra donc utiliser les fonctions printf() et scanf() du C
standard pour afficher des donnes l'cran. De mme les fonctions de flux standard C++ cin et cout
sont utilisables.
Cette partie ne fera pas l'objet d'un projet, mais les applications console seront utilises plus
tard pour illustrer des programmes simples et ne ncessitant pas ou peu de communications avec
l'utilisateur, de manire rduire la taille des programmes.
8. Fentre classique ou ressource ?
Cours thorique :
Maintenant que nous avons cr et utilis ces deux types de fentres, il convient de s'interroger
sur leur utilisation. Faut il prfrer les ressources ou plutt les fentres classiques ? Quels sont les
avantages des une et des autres ?
Page 20 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Les fentres cres partir de ressources (souvent les botes de dialogue) sont videmment
bien plus rapides crer. De plus leur utilisation ncessite beaucoup moins de code. Dans ce cas,
pourquoi continuer utiliser des fentres classiques cres avec l'API Windows ?
Si les ressources peuvent sduire, il faut cependant s'en mfier. Elles conviennent trs bien
pour des botes de dialogue invitant l'utilisateur saisir des valeurs, ou dfinir certains paramtres.
C'est d'ailleurs leur rle principal. En les utilisant dans ce contexte on gagne un temps trs important
par rapport des fentres cres 'manuellement'. Cependant, l'affichage dans une bote de dialogue
ne se prte que peu la personnalisation.
Pour afficher des donnes comme des images, des polices personnalises ou autre, il reste
prfrable d'utiliser une fentre classique. De mme si la fentre doit grer des interventions
utilisateur comme une touche entre, un clic, il faut alors passer une fentre classique. Dans ces
cas, la gestion automatise devient en fait un obstacle l'interception de ces vnements.
Avant de se lancer dans la programmation d'une fentre classique ou au contraire de fentres
ressources, il faut donc rflchir l'utilisation qui sera faite de ces fentres. Ni l'une ni l'autre de ces
mthodes ne sont bannir. Au contraire, il faut connatre les deux et savoir utiliser l'une ou l'autre
ds qu'il le faut. La facilit des ressources ne doit pas en faire un rflexe systmatique.
Bien entendu on ne peut pas tablir des caractristiques types pour lesquelles il faut
absolument utiliser tel ou tel type de fentre. Cette dcision doit prendre en compte les exigences du
programme... Je ne me risquerais pas dresser une liste exhaustive de tous les cas ou l'utilisation de
telle ou telle mthode est prfrable mais j'invite seulement le programmeur rflchir avant
d'utiliser les ressources.
Chapitre 2
Les botes de dialogue
1. Fonctionnement gnral
Cours thorique :
Avant de se lancer dans la programmation d'applications plus complexes, il est ncessaire de
bien comprendre le schma de fonctionnement de la bote de dialogue. Il se rapproche du
fonctionnement d'une fentre classique sans pour autant tre le mme. Il conviendra donc d'viter les
analogies entre ces deux types de fentres.
La procdure qui gre une bote de dialogue peut tre excessivement simple. Pour toute
notification, Windows appelle la procdure et lui demande de traiter le message. Si elle est en
mesure de le traiter, elle doit retourner TRUE. Dans le cas contraire elle retourne FALSE et
Windows effectuera le traitement par dfaut pour le message. Le minimum est donc d'assurer la
fermeture de la boite de dialogue grce la fonction EndDialog(). En effet, la fermeture de la boite
Page 21 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
de dialogue ne fait pas partie du traitement par dfaut des messages, ce qui est logique puisqu'il
est impossible Windows de 'deviner' le bouton qui doit quitter la bote de dialogue.
Ds qu'une boite de dialogue traite un message, elle doit donc retourner TRUE (sauf indication
contraire). Remarquez la structure du switch avec le default qui retourne FALSE. Ds qu'un message
n'est pas trait, la procdure retourne donc FALSE.
On peut considrer le fonctionnement d'une bote de dialogue en trois temps. Tout d'abord
l'initialisation. C'est ce moment que les diffrents contrles sont initialiss. Par exemple, le statut
des 'check boxes' sera dfini. Cette phase est gnralement indispensable, mais dans certains cas, une
bote de dialogue n'aura besoin d'aucune valeur par dfaut. L'initialisation se fait avant que la boite
de dialogue ne soit affiche, grce un message (WM_INITDIALOG). Si ce message n'est pas
trait, c'est dire si la procdure ne contient pas de 'case WM_INITDIALOG', aucun champ n'aura
de valeur particulire.
La deuxime phase de fonctionnement d'une boite de dialogue est celle durant laquelle
l'utilisateur effectue les modifications qu'il dsire. Il modifie des champs, 'coche' des options... Ce
traitement peut se faire de manire entirement autonome si aucune action particulire n'est
ncessaire. Cette phase ne se refltera donc pas forcment dans la procdure. Dans certains cas cette
phase sera inutile, par exemple pour un message d'erreur.
Une fois que l'utilisateur a fait les modifications qu'il dsire, il va falloir les 'rcuprer'. Le
statut de tous les contrles doit tre analys de manire modifier les variables au sein mme du
programme. En effet, les modifications effectues par l'utilisateur ne se refltent en rien au niveau
des variables du programme. Une fois que le programme mis jour toutes les variables ncessaires,
la boite de dialogue peut se terminer.
2. Initialisation
Cours thorique :
L'initialisation d'une bote de dialogue se fait grce au message WM_INITDIALOG. Si une
bote de dialogue ne dsire pas effectuer de traitement particulier sa cration, elle ignore
simplement ce message. Lorsque ce message est envoy la boite de dialogue, celle-ci n'est pas
encore affich. C'est ce moment que les valeurs par dfaut des contrles sont dfinies. Ces valeurs
seront dfinies grce des fonctions de l'API Windows.
De plus, comme la fentre n'est pas encore visible, il est possible de modifier sa taille, sa
position, sans que cela apparaisse l'utilisateur (fonction SetWindowPos() par exemple).
La valeur retourne aprs le traitement du message WM_INITDIALOG dtermine si le champ
par dfaut aura ou non le focus. Le focus est en fait l'entre clavier. Si un champ possde le focus et
qu'une touche est tape au clavier, c'est ce champ et lui seul que cela sera signal. Dans un champ
demandant un mot de passe (par exemple), il est trs utile de passer la focus au champ par dfaut, de
manire viter l'utilisateur de devoir cliquer dans le champ avant de saisir sont mot de passe. Pour
Page 22 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
que Windows donne le focus au champ par dfaut, la procdure doit retourner TRUE. Dans le
cas contraire, elle doit retourner FALSE.
3. Le contrle 'Edit'
Cours thorique :
Ce contrle dj t vu dans les projets prcdents. C'est un des plus simples utiliser.
Comme pour tous les contrles, on peut distinguer deux aspects l'utilisation de celui-ci. Le premier
aspect est son apparence. Elle est modifie grce l'diteur de ressources du compilateur. Si vous
utilisez Visual C++, il vous suffit de double cliquer sur le contrle pour accder ses proprits. La
deuxime partie du fonctionnement de ce contrle se fait au travers de la procdure qui gre la bote
de dialogue. Elle consiste en la dfinition ou la rcupration de l'tat du contrle (ici le texte qu'il
contient).
Avant d'utiliser le contrle, il faut donc dfinir son apparence, ainsi qu'un identifiant,
permettant au programme de l'identifier. L'identifiant d'un contrle est gnralement de la forme
'IDC_' suivi du nom du contrle tout en majuscules. Le compilateur se charge de maintenir jour un
fichier dfinissant toutes les constantes utilises. Pour Visual C++, ce fichier est 'ressource.h'.
Il est possible de personnaliser de nombreuses caractristiques pour un contrle de type 'Edit'.
Voici une liste regroupant les styles les plus utiliss. Cette liste n'est pas exhaustive, rfrez vous
l'annexe A pour plus de prcisions. Si vous utilisez un diteur de ressources, vous devriez pouvoir
modifier les styles grce une interface graphique. Si ditez vos ressources en mode texte, les styles
sont mis entre crochets.
Read Only [ES_READONLY] : Afficher le contrle sur fond gris. Il est impossible de
modifier le texte, mais il peut toutefois tre slectionn et copi.
Disabled : Le contrle est dsactiv. Il apparat en gris et il est impossible de slectionner le
texte. Cette option ne sera accessible qu' partir d'un diteur de ressources car elle ne constitue pas
un style. Pour dsactiver un contrle, utilisez la fonction EnableWndow() lors de l'initialisation de la
bote de dialogue.
Number [ES_NUMBER] : Indique que l'on ne peut saisir que des chiffres dans ce contrle.
Le point n'est pas accept. Pour entrer un nombre virgule, il faudra donc utiliser un contrle
classique.
Password [ES_PASSWORD] : Affiche des toiles la place des caractres saisis. Le texte ne
peut pas tre copi.
Multiline [ES_MULTILINE] : Indique que le contrle peut contenir plusieurs lignes. Pour
effectuer un retour la ligne, il faut afficher le caractre '\r\n'. Au clavier, il faut taper
CTRL+ENTER.
AutoHScroll [ES_AUTOHSCROLL] - AutoVScroll [ES_AUTOVSCROLL] : Indique si le
contrle doit dfiler horizontalement ou verticalement. Pour permettre un contrle de dfiler
Page 23 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
verticalement il faut dsactiver le dfilement horizontal.
Une fois que l'apparence du contrle est convenable, il faut pouvoir lui assigner une valeur ou
rcuprer la valeur entre par l'utilisateur. Ces manipulations se font au travers de 4 fonctions :
SetDlgItemInt() : permet de dfinir la valeur d'un contrle grce un entier. Il est possible
d'indiquer si cet entier est sign ou non. L'identifiant du contrle est celui dfinit dans les ressources
(IDC_... en gnral).
GetDlgItemInt() : permet de rcuprer la valeur numrique d'un contrle. Cette fonction
chouera si le contrle contenait du texte.
SetDlgItemText() : permet d'afficher une chane de caractres dans le contrle. Cette chane
de caractres peut priori avoir n'importe quelle longueur.
GetDlgItemText() : permet de rcuprer la chane de caractres contenue dans le contrle. Il
est indispensable de prciser la longueur maximale de la chane qui sera rcupre (elle correspond
la taille du buffer dans lequel elle sera stocke).
4. Le contrle 'Check Box'
Cours thorique :
Le contrle 'Check Box' fait lui aussi partie de l'un des plus simples. Il peut contenir trois tats,
coch, non coch ou indtermin. On peut lui attribuer diffrents styles qui modifient son apparence
et parfois son fonctionnement. Par dfaut, ce contrle ne peut tre que dans deux tats, coch ou non
coch. En modifiant le style on peut permettre l'utilisateur de spcifier un troisime tat,
indtermin, qui correspond une case coche mais grise. Voici une liste des styles principaux
pouvant tre appliqus ce contrle (les styles modifiant l'apparence graphique du contrle ne sont
pas traits ici). Pour une liste exhaustive des diffrents types consultez l'annexe A.
Tri-state [BS_3STATE] [BS_AUTO3STATE] : le contrle peut prendre 3 tats au lieu de
deux.
Auto : [BS_AUTO3STATE] [BS_AUTOCHECKBOX] : permet de crer un contrle dont
l'tat est modifi automatiquement lorsque l'utilisateur l'utilise. Si ce style n'est pas activ, la bote de
dialogue reoit un message WM_COMMAND et doit elle-mme modifier l'tat du contrle.
Comme on pouvait s'y attendre, il suffit de deux fonctions pour le manipuler, une pour
modifier son tat, et l'autre pour rcuprer son tat.
CheckDlgButton() : permet de modifier l'tat du contrle. Il suffit de spcifier la boite de
dialogue qui le contient ainsi que son identifiant et un paramtre commandant l'tat du contrle. Les
trois tats possibles sont BST_CHECKED, BST_INDETERMINATE et BST_UNCHECKED. Le
contrle sera redessin automatiquement, c'est dire que lorsque la fonction retournera, l'tat
graphique du contrle aura t modifi.
IsDlgButtonChecked() : permet de rcuprer l'tat du contrle. Cette fonction retourne un
Page 24 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
entier non sign qui peut prendre une des trois valeurs prsentes ci-dessus.
L'utilisation de contrles 'Check box' se rsume donc peu de choses. Elle pourra tre mise en
parallle avec les contrles 'Radio buttons', qui fonctionnement de manire similaire, mais avec un
systme de groupes.
5. Le contrle 'Radio Button'
Cours thorique :
Le contrle 'Radio Button' est assez similaire au contrle 'Check Box', si ce n'est qu'un seul de
ces contrles peut tre coch la fois. Il se prsente gnralement sous forme d'un rond cocher.
Bien entendu, vous pouvez utilisez plusieurs de ces contrles dans un mme fentre et crer des
groupes, de sorte qu'un seul des contrle par groupe ne puisse tre slectionn, et non pas un seul
pour l'ensemble de la fentre.
Si vous dsirez crer seulement un groupe dans une fentre, il est inutile de crer un groupe.
En effet, par dfaut, un seul contrle peut tre slectionn dans l'ensemble de la fentre.
Si vous utilisez l'diteur de ressources de Visual, la cration de groupes est simplifie. Pour
crer un groupe de radio buttons, il faut en insrer un, et lui mettre le style 'Group'. Puis on insre les
autres radios du groupe mais sans mettre le style 'Group'. Pour crer un nouveau groupe, il suffit de
refaire la mme manipulation autant de fois que ncessaire.
Si vous ditez vos ressources en mode texte, le principe est le mme. On met le style
WS_GROUP au premier des radios puis on insre les autres la suite.
L'ensemble des styles est dcrit dans l'annexe A. La plupart modifient seulement l'apparence
graphique du contrle et ne sont pas trs difficiles utiliser. Retenez simplement un style assez
important :
Auto [BS_AUTORADIOBUTTON] : ce contrle indique que les radios sont grs
automatiquement. Vous n'avez pas demander de dcocher les radios lorsqu'un autre est coch.
Gnralement, ce style est toujours utilis, sauf cas trs particuliers.
Pour cocher ou dcocher les radios, il faut utiliser les fonctions ChekDlgButton() et
IsDlgButtonChecked(). Ces fonctions sont identiques celles utilises avec les contrles 'Check
Box'. Bien entendu, le style indtermin n'est pas disponible ici. Remarquons que lorsque vous
utilisez CheckDlgButton(), les autres radios ne sont pas dcochs, mme en utilisant le style Auto.
Enfin, vous pouvez utilisez la fonction CheckRadioButton() pour cocher un radio et dcochez les
autres. Pour cela, les radios doivent avoir ts crs dans l'ordre (les identifiants doivent tre en ordre
croissant). Il suffit ensuite de passer l'identifiant du premier radio, celui du dernier, ainsi que le radio
slectionner.
6. Le contrle 'List Box'
Page 25 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Le contrle 'List Box' est plus complexe que ceux tudis prcdemment. Il ne permet pas
seulement de contenir un tat entr par l'utilisateur, il permet d'afficher des informations sous forme
de liste, de trier la liste par ordre alphabtique Ce contrle aura donc de multiples utilisations,
d'autant plus qu'il est relativement simple utiliser. Il ne se manipule pas grce des fonctions mais
au travers de messages, qui seront envoys par l'intermdiaire de la fonction SendMessage().
La liste peut tre prsente sous deux formes : soit en une seule colonne et avec un dfilement
vertical, soit sur plusieurs colonnes et avec un dfilement horizontal. Les colonnes auront
obligatoirement la mme taille. Elles ne peuvent pas comporter d'enttes.
Voici les styles applicables ce contrle. Pour une liste exhaustive, consultez l'annexe A.
Remarquez que par dfaut, c'est la slection simple qui est utilise.
Sort [LBS_SORT] : dtermine si le contrle trie ou non la liste par ordre alphabtique.
Multi-Column [LBS_MULTICOLUMN] : permet d'afficher la liste sur une ou plusieurs
colonnes. Si ce style est activ, il faut utiliser un dfilement horizontal.
Horizontal Scroll [WS_HSCROLL] : dfilement horizontal de la liste.
Vertical Scroll [WS_VSCROLL] : dfilement vertical de la liste.
Multiple Selection [LBS_MULTIPLESEL] : autorise la slection multiple. L'utilisateur peut
slectionner les lments de la liste en cliquant dessus.
Extended Selection [LBS_EXTENDEDSEL] : autorise la slection multiple. L'utilisateur
slectionne les lments de la liste grce aux touches SHIFT et CTRL.
No Selection [LBS_NOSEL] : interdit toute sorte de slection.
L'utilisation du contrle est relativement simple. Nous ne verrons ici que les tches
lmentaires, ajout et suppression d'un lment, rcupration de la slection courante. La mthode de
rcupration de la slection courante dpend bien entendu du mode de slection choisi. Pour une
slection unique, cette action est simple. Elle s'avre un peu plus dlicate pour un contrle
slections multiples. L'ensemble des messages utilisables avec ce contrle est prsent dans l'annexe
A. Voici les messages les plus simples :
LB_ADDSTRING : ajoute un lment la liste et demande le tri de la liste si celle-ci le
style 'Sort'.
LB_INSERTSTRING : ajoute un lment la liste et le place une position dtermine.
Aprs l'insertion, la liste n'est pas trie.
LB_SETITEMDATA : permet d'associer une valeur 32 bits un lment de la liste. Cette
valeur peut par exemple tre un pointeur sur les donnes que reprsente cet lment.
LB_DELETESTRING : supprime l'lment dsign.
LB_GETCURSEL : retourne la slection courante pour un contrle slection unique.
Page 26 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
LB_GETSELITEMS : place dans un tableau l'ensemble des lments slectionns dans un
contrle slections multiples.
LB_SETCURSEL : modifie la slection courante dans un contrle slection unique.
LB_GETCOUNT : retourne le nombre total d'lments contenus par le contrle.
7. Le contrle 'Progress bar'
Cours thorique :
Le contrle 'Progress Bar' est l'un des plus simples utiliser. Ses styles tant dfinis au niveau
ressources, l'utilisation se borne la dfinition de la position. Pour cela, il faut tout d'abord donner 2
valeurs numriques qui constituent le minimum et le maximum de la barre de progression. Une fois
cela fait, il ne reste plus qu' fournir une position courante.
Avant de pouvoir utiliser ce contrle, il faut penser l'initialiser. On doit placer en dbut de
programme un appel la fonction InitCommonControls() ou la fonction InitCommonControlsEx(),
de manire forcer le chargement des Dlls permettant d'utiliser ce contrle. L'appel l'une de ces
deux fonctions peut tre fait n'importe quel moment (mais avant d'utiliser le contrle) et ne doit
tre fait qu'une seule fois. Si cet appel n'est pas fait, la bote de dialogue de s'affichera pas.
Les styles pouvant tre utiliss avec ce contrle sont simples, ils sont donns dans l'Annexe A.
Voici les messages utiliss pour commander ce contrle :
PBM_SETRANGE permet de dfinir les valeurs numriques du minimum et du maximum
pouvant tre atteints. Les valeurs de cet intervalle, tout comme la position courante sont des entiers,
il faut donc prvoir un intervalle assez large pour ne pas que l'on distingue de saccades lors de la
progression. Cet intervalle doit donc tre au moins de la taille (en pixels) de la barre. De manire
gnrale, on peut opter pour un intervalle 0 - 1000.
PBM_SETPOS dfinit la position actuelle de progression. Cette valeur doit bien entendu tre
comprise dans l'intervalle fourni par le message PBM_SETRANGE.
8. Le contrle 'Combo box'
Cours thorique :
Le contrle 'Combo Box' est l'un des plus complexes. Il peut servir dans diffrents 'modes de
fonctionnement'. On peut l'utiliser pour permettre l'utilisateur de faire un choix dans un menu
droulant ou permettre l'utilisateur d'entrer un choix personnalis. Nous ne verrons ici que
l'utilisation de ce contrle en tant que menu droulant permettant l'utilisateur de faire un choix dans
une liste prdtermine. C'est l'utilisation la plus pratique de ce contrle car elle permet de s'assurer
que le choix fait est valide.
Page 27 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Voici un aperu des styles applicables ce contrle :
CBS_DROPDOWN est le style correspondant au type de 'Combo box' que nous avons dcrit.
La liste est affiche si l'utilisateur clique sur le contrle et elle ne peut pas tre modifie.
CBS_SORT permet d'activer le tri automatique du contrle par ordre alphabtique.
Pour utiliser ce contrle, vous pouvez utiliser les messages suivants :
CB_ADDSTRING permet d'ajouter une entre la liste. La liste sera trie si le style
CBS_SORT est utilis.
CB_SETCURSEL permet de dfinir la slection courante.
CB_GETCURSEL permet de retourner la slection courante.
CB_SETITEMDATA permet d'associer une valeur 32 bits une entre. Cette valeur peut tre
utilise pour identifier les entres si la liste est trie par exemple.
CB_GETITEMDATA retourne la valeur 32 bits associe avec l'entre spcifie.
Pour dterminer la taille de ce contrle (notamment la hauteur de la liste), on pourra utiliser la
fonction SetWindowPos() qui sera vue plus tard.
9. Synthse sur les contrles
Cours thorique :
Les contrles qui ont t prsents prcdemment l'ont t de manire brve. Ce tutorial ne
prtend absolument pas fournir une rfrence exhaustive de toutes les possibilits d'utilisation des
contrles. Cette prsentation des contrles les plus courants permettra simplement de se familiariser
avec leur utilisation. En effet, quel que soit le type de contrle utilis, la mthode pour le manipuler
reste toujours la mme.
Une fois le principe de fonctionnement des contrles compris, il sera facile de l'tendre une
utilisation plus complexe. Les contrles prdfinis offrent une excellente manire de guider
l'utilisateur et de le limiter. De plus, leur utilisation demande relativement peu de code et permet de
crer rapidement une interface agrable. Les contrles offrent de nombreuses fonctionnalits qu'il
faut savoir exploiter.
On remarquera cependant que ces contrles sont assez difficilement personnalisables. Pour
crer une interface graphique plus pousse ou plus personnalise, il faudra donc recourir d'autres
mthodes que des contrles. Il est tout de mme possible de personnaliser les contrles (couleurs,
etc...) mais ces mthodes ne permettant une personnalisation complte de l'interface.
Il faut tout de mme rappeler que les ressources ne sont pas le seul moyen de mettre en place
les contrles, ils constituent simplement un 'raccourci' pour leur utilisation. Tous les contrles
peuvent tre intgrs de manire dynamique dans une fentre ou un bote de dialogue en utilisant la
fonction CreateWindowEx(). Dans le cas d'une interface dynamique, c'est dire ne se prsentant
pas systmatiquement sous la mme forme, l'utilisation des ressources est fortement dconseille car
Page 28 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
elle 'fixe' les contrles au moment de la compilation. La cration des contrles au moment de
l'affichage permet donc une plus grande souplesse, bien qu'exigeant une mise en place plus
contraignante.
10. Deux projets rcapitulatifs
Cours thorique :
Voici 2 projets qui prsentent des utilisations extrmement simples de quelques contrles
lmentaires. Seul le projet 06a sera dcrit ici, le second tant trs similaire.
Projet N6a :
Ces projets prsentent une utilisation extrmement simple de quelques contrles lmentaires.
Aucune vrification n'est faite quant la validit des entres faites par l'utilisateur. Remarquons que
du fait des contrles utiliss, l'utilisateur ne peut pas fournir de slection non valide ( part entrer un
nom vide). C'est d'ailleurs pour a qu'il est indispensable d'initialiser les contrles 'Radio' ou 'Combo
box' car sans cela, on pourrait avoir une slection non valide (aucune slection en fait). Les contrles
sont bien entendus initialiss lors du message WM_INITDIALOG ( ce moment, la bote de
dialogue n'est pas encore affiche).
La liste n'est modifie qu'au moment o l'utilisateur presse le bouton 'Ajouter'. A ce moment,
on rcupre le statut de tous les contrles que l'on a placs et on vrifie ventuellement la validit des
donnes entres. Si tout est correct, on ajoute les donnes voulues la liste. Ici, on aurait quelques
peines rcuprer les informations contenues dans la liste au moment de la validation de la bote de
dialogue. Pour faciliter cela, il faudrait stocker chaque ligne dans une structure approprie. On
associerait ensuite chaque entre de la liste son index dans notre tableau grce au message
LB_SETITEMDATA.
Tlcharger les projets comments [VC++] : Projet 06a - Projet 06b.
Tlcharger les projets comments [BCB] : Projet 06a [BCB] - Projet 06b [BCB].
11. MessageBox()
Cours thorique :
La fonction MessageBox() permet d'afficher une boite de dialogue prdfinie contenant le
texte spcifi. Elle est trs utile pour afficher des messages d'erreurs, informer l'utilisateur. Elle
retourne l'identifiant du bouton qui a t press pour la quitter. Cette fonction ne permet quasiment
aucune personnalisation de la bote de dialogue qu'elle affiche, elle n'est donc rellement utile que
dans le cas de messages envoys l'utilisateur, souvent de manire informative avec pour seul choix
Page 29 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
le bouton 'Ok'.
Les types de boites de dialogue pouvant tre affichs sont :
MB_ABORTRETRYIGNORE
MB_OK
MB_OKCANCEL
MB_RETRYCANCEL
MB_YESNO
MB_YESNOCANCEL
Les icnes suivantes peuvent tre affiches sur la bote de dialogue :
MB_ICONEXCLAMATION
MB_ICONWARNING
MB_ICONINFORMATION
MB_ICONASTERISK
MB_ICONQUESTION
MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND
La fonction retournera l'une des valeurs suivantes :
IDABORT
IDCANCEL
IDIGNORE
IDNO
IDOK
IDRETRY
IDYES
12. Parcourir l'arborescence
Cours thorique :
Pour permettre l'utilisateur de slectionner un dossier ou un fichier, on utilisera
respectivement les fonctions SHBrowseForFolder() et GetOpenFileName(). Ces deux fonctions
affichent les botes de dialogue standard de Windows pour parcourir l'arborescence la recherche
d'un dossier ou d'un fichier. Notons que la constante MAX_PATH dsigne la longueur standard
pour un chemin. Les buffers chargs de recevoir les noms de fichiers ou les chemins sont supposs
Page 30 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
faire au moins cette taille.
Voici un exemple d'utilisation de la fonction SHBrowseForFolder() :
BROWSEINFO bi;
LPITEMIDLIST Item;
// Ici, la taille du buffer ne peut pas tre passe
// buffer est suppos tre de taille MAX_PATH (ou plus)
char buffer[MAX_PATH];

// On met tous les champs inutiliss 0
memset(&bi,0,sizeof(BROWSEINFO));
// hDlg est le HWND de la boite de dialogue qui demande l'ouverture
// Ou NULL si la boite de dialogue n'a pas de fentre parent
bi.hwndOwner=Dlg;
// Contient le rpertoire initial ou NULL
bi.pidlRoot=NULL;
bi.pszDisplayName=buffer;
bi.lpszTitle="Rpertoire courant";
bi.ulFlags=NULL;
bi.lParam=NULL;
Item=SHBrowseForFolder(&bi);
if(Item!=NULL)
{
// buffer contient le nom du rpertoire slectionn
SHGetPathFromIDList(Item,buffer);
// buffer contient le chemin complet de la slection
}
Voici un exemple d'utilisation de la fonction GetOpenFileName() :
OPENFILENAME st;
char buffer[MAX_PATH];

// Pas de fichier par dfaut
buf[0]='';
// On met tous les champs inutiliss 0
memset(&st,0,sizeof(OPENFILENAME));
st.lStructSize=sizeof(OPENFILENAME);
// hDlg est le HWND de la boite de dialogue qui demande l'ouverture
// Ou NULL si la boite de dialogue n'a pas de fentre parent
st.hwndOwner=hDlg;
// La syntaxe est : Description1Filtre1Description2Filtre2
st.lpstrFilter="Executables - Fichiers de commandes*.exe;*.bat";
st.lpstrFile=buffer;
st.nMaxFile=MAX_PATH;
st.lpstrTitle="Recherche de l'excutable";
st.Flags=NULL;
// Contient le rpertoire initial ou NULL
st.lpstrInitialDir=NULL;
if(GetOpenFileName(&st))
// buffer contient notre chemin
Chapitre 3
Les fentres
1. Fentres et botes de dialogue
Page 31 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Les botes de dialogue sont semblables aux fentres dans le sens o ce sont des fentres.
Cependant, il faut tre bien attentif marquer la diffrence qu'il existe entre ces deux types de
fentres. Les botes de dialogue sont gres en grande partie par le systme. Crer une boite de
dialogue est relativement simple. Les couleurs de fond, le type de fentre ont des valeurs par dfaut
si l'on utilise un diteur de ressources.
Dans le cas des fentres, il va falloir tout dfinir lors de l'excution. Lors de la cration d'une
fentre, il sera ncessaire de prciser un grand nombre de paramtres qui taient passs
implicitement au systme grce aux ressources avec les botes de dialogue. Il faut donc bien
considrer les botes de dialogue comme un cas 'simplifi' de fentres.
Cependant, on ne peut pas appliquer toutes les mthodes fonctionnant sur les fentres aux
botes de dialogues ou inversement. Le mode de gestion des botes de dialogue est diffrent de celui
des fentres, particulirement au niveau de l'affichage. De manire gnrale, il faut utiliser les botes
de dialogue avec des contrles prdfinis. Pour crer des fentres contenant un affichage
personnalis, des images, des graphiques, du texte utilisant diffrentes polices, il est prfrable
d'utiliser une fentre. C'est donc pour ce type de travaux que les botes de dialogues trouvent leurs
limites.
Il faut donc retenir que les fentres et les botes de dialogue ne fonctionnement pas de la mme
manire (malgr de nombreuses similitudes). Copier - Coller la procdure d'une fentre pour une
bote de dialogue ne serait pas une bonne ide car certains messages sont spcifiques aux fentres et
d'autres aux botes de dialogue.
2. Fonctionnement gnral d'une fentre
Cours thorique :
Le fonctionnement gnral des fentres a dj t vu au cours du premier chapitre. Un petit
rappel ainsi que quelques prcisions est cependant ncessaire.
Lorsqu'une fentre est cre, le systme lui attribue un identifiant. Cet identifiant est appel
'handle'. Il identifi la fentre dans son ensemble. Toute modification de la fentre (dplacement,
modification de la taille) se fait grce cet identifiant. Une application utilisant plusieurs fentres
pourra donc tre amene dfinir des variables globales contenant les identifiants des diffrentes
fentres pour plus de confort. Cette dfinition globale n'est absolument pas ncessaire et peut tre
vite dans le cas d'applications simples (le passage en paramtre des identifiants devient rapidement
impossible).
La fentre d'une application ne correspond absolument pas l'application elle mme. Une
application (ou processus) peut tre excute et ne pas avoir de fentre. La cration de la fentre est
Page 32 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
effectue par le programme lui mme. De plus, elle est totalement facultative. Aucune fentre
ne sera cre par le systme au moment de l'excution de l'application. De plus, la fermeture de la
fentre n'entrane pas la fermeture de l'application. C'est au programmeur de faire concorder ces deux
vnements si il le dsire. Bien entendu la majorit des applications crent une fentre au dmarrage
et se terminent lors de la fermeture de cette mme fentre, mais ce n'est en rien une obligation. De
plus, une fentre peut avoir plusieurs tats, visible, cache, minimise... Lorsqu'une fentre est cre,
elle n'est pas visible. Elle existe, ont peut la dplacer, afficher dans sa zone client... Cependant, rien
de tout ceci n'est visible l'utilisateur. Ds qu'on donne une fentre le statut 'visible', elle apparat
la mme position que lorsqu'elle tait invisible. Lorsqu'on dtruit une fentre, elle n'est plus affiche
l'cran, mais dans ce cas, on ne peut plus la modifier. Elle n'existe plus pour le systme. Il ne faut
donc pas confondre une fentre cache, qui existe toujours bien que n'tant pas visible, et une fentre
dtruite, qui elle n'existe plus.
Une fentre contient diffrentes zones. La zone client est la zone (gnralement blanche) dans
laquelle les donnes de la fentre sont affiches. Les menus, la barre de titre, ne font pas partie de la
zone client. La zone client est la seule partie de la fentre qui soit gre par l'application. La taille de
la fentre sur l'cran est donc diffrente de celle de la zone client. La zone client est ncessairement
de taille gale ou infrieure l'espace occup par la fentre. Pour afficher des donnes dans une
fentre, on utilise des coordonnes relatives la zone client. Pour dplacer une fentre, on utilise des
coordonnes relatives l'cran (coin suprieur gauche).
Pour identifier la zone client, on utilise un contexte d'affichage. Il dfinit l'ensemble des
proprits relatives l'affichage dans la zone client (nombre de couleurs disponibles, type de
police...). Un contexte d'affichage identifie une zone dans laquelle ont peut afficher n'importe quel
type de donnes. Cette zone n'est pas ncessairement affiche l'cran, on peut par exemple afficher
des donnes dans une fentre cache en utilisant le mme contexte d'affichage. Dans ce cas, le
contexte d'affichage est li la fentre, on peut donc l'obtenir partir de l'identifiant de la fentre
concerne. Pour afficher dans la zone client, il n'est donc jamais ncessaire de se proccuper de la
position de la fentre sur l'cran, il suffit de possder un 'handle' sur le contexte d'affichage.
3. Rcupration des messages
Cours thorique :
La rception des messages et leur transmission la procdure de fentre approprie sont
essentiels. L'application ne doit jamais cesser 'd'couter' au cas o de nouveaux messages
arriveraient. Si l'application arrte de transmettre les messages, la fentre semblera
'bloque' (affichage persistant, impossibilit de la dplacer...).
La rcupration des messages peut se faire grce deux fonctions : GetMessage() ou
PeekMessage(). La diffrence entre ces deux fonctions est fondamentale, il ne faut donc pas les
assimiler.
Page 33 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
GetMessage() est une fonction bloquante. Elle attend qu'au moins un message soit prsent
dans la file d'attente de la fentre pour retourner. Le temps d'attente est infini, tant qu'aucun message
n'est plac dans la file d'attente.
PeekMessage() est une fonction non bloquante. Elle vrifie si des messages sont prsents et
retourne immdiatement. Si au moins un message tait prsent, elle le retourne, sinon elle ne
retourne aucun message. De plus, cette fonction permet de ne pas supprimer les messages de la file
d'attente. On peut donc consulter des messages et laisser la boucle de messages principale effectuer
le traitement.
L'utilisation de ces deux fonctions s'inscrit dans des cadres totalement diffrents. En gnral,
GetMessage() est utilis dans une boucle 'while'. Si aucun message n'est prsent, GetMessage()
signale au systme qu'il peut donner le processeur une autre application. Placer cette fonction dans
une boucle ne gnre donc pas d'occupation processeur tant qu'aucun message n'est prsent dans la
file d'attente. PeekMessage() ne fonctionne pas de cette manire. Placer cette fonction dans une
boucle 'while' gnre une occupation processeur de 100%. La fonction sera effectivement appele un
nombre indfini de fois, occupant ainsi le processeur. La rcupration principale des messages doit
donc se faire avec la fonction GetMessage().
Cependant, si l'application est occupe, elle peut appeler de manire rgulire PeekMessage()
suffisamment de fois pour traiter tous les messages. Par exemple, toute les secondes, PeekMessage()
est appele jusqu' puisement de la file d'attente. Le traitement des messages sera plus lent mais il
sera effectu quand mme. Cette solution vite de devoir faire appel au multi-threading (excution
simultane de plusieurs parties du programme).
La diffrence entre ces deux fonctions doit donc tre clairement comprise. L'utilisation de l'une
ou l'autre de ces fonctions tort peut s'avrer catastrophique pour les performances du programme et
du systme dans son ensemble. Dans le cas d'une application n'effectuant pas de tches lourdes,
l'utilisation de PeekMessage() ne sera pas ncessaire. L'interruption du traitement des messages pour
une dure courte (jusqu' un dixime de secondes) est sans consquences. Il faut tout de mme
remarquer que GetMessage() ne sera pas rappele tant que le traitement du message courant n'est
pas termin. Le traitement des messages par la procdure doit donc si possible tre bref.
Voici un exemple classique de boucle ralisant le traitement de messages :
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
La fonction GetMessage() retourne FALSE si le message WM_QUIT est reu. Ce message
est envoy la fentre principale pour demander l'arrt de l'application. Par dfaut, ce message n'est
pas envoy lors de la destruction de la fentre.
4. Cration d'une fentre
Page 34 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Avant de pouvoir crer une fentre, il faut dfinir un type. C'est ce type de fentre qui sera
utilis pour afficher la fentre nouvellement cre. Il est possible d'utiliser des types prdfinis ou de
crer son propre type de fentres. Les contrles utiliss par les boites de dialogues sont des types de
fentres prdfinis. Pour dfinir un nouveau type de fentres, on utilise la fonction RegisterClassEx
() (cf. chapitre 1).
Une fois le type de fentre dfinit, il faut crer la fentre. Pour cela, on utilise la fonction
CreateWindowEx() (cf. chapitre 1). On lui passe le type (la classe) de fentre dsir ou encore une
classe prdfinie. Lorsque la fentre est cre, un message WM_CREATE sera envoy. C'est ce
moment que l'application doit faire les initialisations qu'elle dsire.
La fentre cre est initialement cache. Il faut donc faire un appel ShowWindow() pour
l'afficher. Il est possible de passer ShowWindow() le paramtre nCmdShow que Windows passe
WinMain(), de cette manire l'tat de base de la fentre peut tre paramtr dans un raccourci.
5. Destruction d'une fentre
Cours thorique :
La destruction d'une fentre se traduit par l'envoi la fentre concerne d'un message
WM_DESTROY. Il ne faut pas confondre ce message avec le message WM_QUIT. WM_QUIT
demande la fermeture totale de l'application, le message WM_DESTROY demande simplement la
destruction de la fentre en question. Le fait de cliquer sur la 'croix' provoque l'envoi d'un message
WM_DESTROY la fentre. Pour faire quitter l'application en mme temps que la fermeture de la
fentre, on peut utiliser la fonction PostQuitMessage() qui elle, envoie un message WM_QUIT.
Cette fonction doit tre appele ds la rception du message WM_DESTROY. Le 'handle' de la
fentre devient invalide ds la destruction de la fentre. Pour tester la validit d'un 'handle', on peut
utiliser la fonction IsWindow().
Lorsque ce message est reu, la fentre est dj en cours de destruction. Il est possible de
traiter le message WM_CLOSE pour demander une autre action que la destruction de la fentre. On
peut par exemple, masquer la fentre. Une pression sur la 'croix' provoquera donc simplement la
disparition l'cran de la fentre, mais pas au niveau systme. La fentre pourra par la suite tre
raffiche dans le mme tat, par un appel la fonction ShowWindow().
6. Contexte d'affichage
Cours thorique :
Page 35 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Un contexte d'affichage (DC) identifie une zone dans laquelle l'application peut afficher tout
type de donnes. La zone identifie peut tre situe n'importe quel endroit de l'cran, elle peut tre
dplace, ou mme masque par d'autres fentres, sans que l'application en ai conscience. Le fait de
dplacer une fentre ne change donc rien la manire dont l'application redessine son contenu. Il en
est de mme pour une fentre cache ou n'tant pas au premier plan. Toute fentre possde un
contexte d'affichage associ sa zone client. Pour obtenir ce contexte, on peut utiliser la fonction
GetDC() en spcifiant la fentre concerne. Si on spcifie un paramtre NULL, la fonction retourne
un contexte d'affichage sur la totalit de l'cran. La zone identifie couvre l'ensemble de l'cran et est
situe au dessus de toutes les fentres affiches. Sauf cas trs particulier, on n'utilise jamais le
contexte d'affichage de la zone cran pour dessiner. Il peut tre utilis lors de la cration de bitmaps,
etc...
Le contexte d'affichage contient toutes les informations relatives l'affichage (police, nombre
de couleurs)... Il n'identifie pas ncessairement une zone cran. Il peut par exemple identifier une
feuille dans le cas d'une impression. On pourra donc utiliser les mmes fonctions pour afficher
l'cran ou pour envoyer des donnes imprimables mais en modifiant le contexte d'affichage utilis.
Lorsqu'on modifie une proprit du contexte d'affichage (ex. : la police courante), on dit qu'on
slectionne un objet dans le contexte. Cet slection est effectue grce la fonction SelectObject().
Diffrents types d'objets peuvent tre slectionns dans un contexte d'affichage (polices, images,
pinceaux de dessin...).
7. Gestion de base d'une fentre
Cours thorique :
La gestion d'une fentre se fait deux niveaux : au niveau de la procdure qui gre tous les
messages reus par la fentre et par des fonctions permettant diverses manipulation sur la fentre.
Voici un rapide aperu des fonctions les plus utiles pour manipuler les fentres :
SetWindowText() modifie le texte de la barre de titre.
GetWindowText() retourne le texte de la barre de titre.
SetWindowPos() modifie la taille de la fentre, sa position sur l'cran, ainsi que sa position
par rapport aux autres fentres (Z-order).
GetWindowPlacement() retourne des informations sur le statut courant de la fentre spcifie.
GetClientRect() retourne la taille de la zone client de la fentre.
GetWindowRect() retourne la taille totale occupe par la fentre sur l'cran.
AdjustWindowRect modifie la taille de la fentre spcifie.
GetParent() retourne la fentre parent de la fentre spcifie.
SetParent() modifie la fentre parent de la fentre spcifie.
Page 36 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
ShowWindow() modifie l'tat courant d'une fentre.
IsIconic(), IsZoomed(), IsWindowVisible() retournent des informations sur l'tat de la fentre
courante.
GetWindow() retourne la fentre ayant une relation spcifie dans le Z-order par rapport la
fentre passe en paramtre.
La procdure de fentre, quant elle, gre l'ensemble des messages envoys une fentre.
Comme les messages envoys une fentre sont extrmement nombreux, il serait trs pnible de
devoir tous les traiter, en particulier pour des fentres simples. L'API Windows fournit la fonction
DefWindowProc() qui propose un traitement par dfaut de tous les messages envoys une fentre.
Pour un traitement personnalis du message, il ne faut pas appeler la fonction DefWindowProc(). Le
traitement par dfaut de la plupart des messages est en gnral satisfaisant. Cependant, pour imposer
des comportements personnaliss une fentre, il s'avre obligatoire d'effectuer un traitement
personnalis de certains messages (ex. : pour masquer la fentre lors d'une pression sur la 'croix').
Voici un aperu rapide des messages les plus courants :
WM_CREATE est envoy une fentre au moment de sa cration.
WM_PAINT est envoy lorsqu'une partie de la zone client doit tre redessine.
WM_ERASEBKGND est utilis pour demander l'effacement d'une partie de la zone client.
WM_SYSCOMMAND est envoy pour le traitement de diffrents vnements pouvant
survenir (fentre minimise, restaure, ...).
WM_ACTIVATE est envoy lorsqu'une fentre est active ou dsactive.
WM_MOVE est envoy lorsque la fentre a t dplace.
WM_SIZE est envoy lorsque la taille de la fentre t modifie.
WM_CLOSE indique que l'utilisateur demande la fermeture de l'application (en cliquant sur
la 'croix' ou en pressant ALT+F4).
WM_DESTROY indique que la fentre est dtruite.
Ces messages sont les premiers utiliser pour le maniement d'une fentre. De nombreux autres
seront utiliss pour grer les interactions avec l'utilisateur (souris, clavier). Ils seront vus plus tard.
Dans la plupart des cas, seul un nombre relativement peu lev de messages sera trait par
l'application elle mme. Les autres seront passs la fonction DefWindowProc().
8. Interactions avec l'utilisateur
Cours thorique :
Une fentre, peut recevoir des donnes de la part de l'utilisateur de deux manires : par le
clavier, ou par la souris. Cependant, la fentre ne recevra d'information du systme que si elle est
active. Si la fentre n'est pas active elle ne sera informe ni des vnements clavier, ni des
Page 37 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
vnements souris. La fentre sera informe de diffrents vnements : pression d'une touche,
relchement d'une touche, mouvement de la souris, pression d'un bouton de la souris Une fentre
est informe ds que la souris bouge au dessus de sa zone client (si elle est active). Le nombre de
messages envoys lorsque la souris se dplace est assez important. Aussi, le traitement de ces
messages (WM_MOUSEMOV) doit tre bref.
Les messages suivants sont utiliss pour traiter les vnements provenant du clavier :
WM_KEYDOWN est envoy lorsqu'une touche est enfonce. Ce message indique le 'Virtual
Key Code' correspondant la touche.
WM_KEYUP est envoy lorsqu'une touche est releve. Ce message indique le 'Virtual Key
Code' correspondant la touche.
WM_CHAR indique le caractre ASCII correspondant la touche presse.
WM_SYSKEYUP, WM_SYSKEYDOWN, WM_SYSCHAR sont utiliss pour identifier les
vnements dus des touches systmes.
Remarquons tout de mme la diffrence entre un code ASCII et un 'Virtual Key Code' : le code
ASCII identifie un caractre (a,A,E,* ont des codes ASCII diffrents). Un 'Virtual Key Code'
identifie une touche physique, a et A ont donc le mme code, mais dans le 2e cas, SHIFT est aussi
press (en ASCII SHIFT n'a pas de code, ce qui est normal car cette touche ne correspond pas un
caractre).
Les messages suivants sont utiliss pour traiter les vnements provenant de la souris :
WM_MOUSEMOVE est envoy chaque dplacement de la souris (plusieurs dizaines de
fois par seconde). Les coordonnes de la souris sont passes en paramtres lors de l'envoi de ce
message. Si le message est reu avec un retard, la position reue sera celle enregistre lorsque le
message a t envoy.
WM_LBUTTONDOWN est envoy lorsque le bouton gauche de la souris est press.
WM_LBUTTONUP est envoy lorsque le bouton gauche de la souris est relev.
WM_MBUTTONDBLCLK est envoy lors d'un double clic du bouton gauche de la souris.
Ce message n'est envoy que si le style CS_DBLCLKS est utilis lors de la cration du style de la
fentre.
Les mmes messages sont envoys pour les boutons centraux et droits de la souris.
(WM_MBUTTON_ ou WM_RBUTTON_).
Pour obtenir la position courante de la souris, une application peut utiliser la fonction
GetCursorPos(). Cette position peut ensuite tre convertie grce aux fonctions ScreenToClient() et
ClientToScreen(). Les fonctions GetKeyState() et GetKeyboardState() peuvent tre utilises pour
connatre l'tat d'une touche ou de l'ensemble du clavier.
9. Les timers
Page 38 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Parfois, il peut tre utile d'excuter des tches intervalles fixes. Il existe de nombreux moyens
d'effectuer cela, mais une des mthodes les plus simples est d'utiliser les 'timers' fournis par l'API
Windows. Les 'timers' ne conviennent qu' des rsolutions de temps faibles, en gnral pas plus de
10 Hz. Un message est envoy intervalles rguliers une fentre spcifie. L'utilisation des timers
pour des frquences trop importantes peut nuire aux performances gnrales du systme du fait des
nombreux messages envoyer. De plus, le temps de traitement des messages fait qu'il est impossible
d'obtenir des intervalles rguliers infrieurs 50ms. Les timers ne constituent donc qu'une mthode
simple pour synchroniser un programme dans des conditions assez restreintes. Cependant, ils
conviennent pour la plupart des applications bureautiques.
Les timers sont gnralement utiliss avec des fentres, cependant, ils peuvent tre utiliss sans
fentres. Dans ce cas, le message sera envoy au thread qui a demand la cration du timer. La
fonction GetMessage() doit donc tre appele avec un argument hWnd gal NULL de manire
na pas associer la rception des messages une fentre spcifique.
Pour crer un timer, on utilise la fonction SetTimer() en prcisant l'intervalle de temps dsir.
Un message WM_TIMER sera envoy la fentre chaque intervalle de temps. Pour stopper un
timer, on utilise la fonction KillTimer(). Comme les timers ont des identifiants, il est possible de
crer diffrents timers avec des intervalles diffrents. L'identifiant du timer est pass en paramtre
lors de la rception du message WM_TIMER.
Voici un exemple utilisant un timer sans fentre :
MSG msg;
SetTimer(NULL,NULL,60000,NULL);

while(GetMessage(&msg,NULL,0,0))
{
if(msg.message==WM_TIMER)
MessageBox(NULL,"Dj une minute de pass!","Info",MB_OK);
}
10. Un projet rcapitulatif
Cours thorique :
Le projet prsent ici est simple. Il reprend la mme cration de fentre qu'au chapitre 1.2. La
diffrence rside dans le traitement des messages. Tout d'abord, un timer est utilis pour permettre
l'affichage de la fentre aprs deux secondes seulement. La fentre est initialement cache. Le timer
est cr lors de la rception du message WM_CREATE. Ce n'est pas une ncessit. Il aurait aussi
bien pu tre cr aprs l'appel CreateWindowEx() mais le fait de crer le timer dans la procdure
permet de regrouper l'ensemble des traitements relatifs la fentre dans sa procdure.
Des botes de dialogue sont affiches lorsque divers vnements sont dtects, pression d'un
Page 39 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
bouton (gauche ou droit), pression d'une touche. Les clics hors de la zone client ne sont pas
pris en charge. Comme la position rcupre par GetCursorPos() est relative l'cran, il faut la
convertir en coordonnes relatives la zone client. La fonction ScreenToClient() fournie pas l'API
Windows est donc la plus approprie.
La destruction de la fentre est suivie de la fermeture de l'application grce un appel de
PostQuitMessage().
Tlcharger le projet comment : Projet 07.
11. Affichage de texte dans une fentre
Cours thorique :
Pour afficher du texte dans la zone client d'une fentre, il faut possder un 'handle' sur le
contexte d'affichage de cette fentre. Le texte qui sera affich le sera avec la police courante ainsi
que les couleurs de premier plan et de fond courantes. Pour afficher du texte, il existe diverses
mthodes. La premire est d'utiliser la fonction TextOut(). Cette fonction affiche le texte sur une
seule ligne, sans jamais calculer de retour la ligne. Si le texte dpasse la taille de la fentre, il sera
tronqu. La fonction DrawText() permet quant elle de dfinir un rectangle dans lequel le texte doit
tre crit. Si le texte dpasse la largeur, il sera mis la ligne, et ceci autant de fois qu'il faudra.
La position passe pour dessiner le texte correspond par dfaut au coin suprieur gauche de la
chane de texte qui sera dessine. Le texte sera donc dessin vers la droite et vers le bas par rapport
au point donn. On peut modifier le point de rfrence pour le positionnement du texte avec la
fonction SetTextAlign(). Pour aligner du texte droite, on pourra par exemple placer ce point dans
le coin suprieur droit. Le texte sera donc dessin gauche et en bas de la position passe pour
dessiner le texte.
Les fonctions SetTextColor() et SetBkColor() permettent de dfinir les couleurs courantes de
premier plan et d'arrire plan pour l'affichage du texte. La fonction SetBkMode() modifie les
proprits du fond (transparent ou opaque). Une fois ces fonctions appeles, le texte dessin le sera
systmatiquement avec ces proprits. Pour dessiner du texte en blanc sur fond noir, il suffira donc
d'un seul appel chacune de ces fonctions. Il faudra faire un nouvel appel pour repasser en texte noir
sur fond blanc.
Pour connatre la taille d'un texte l'cran (en pixels), il faut utiliser la fonction
GetTextExtentPoint32(). Cette fonction retourne la taille qu'occupera un texte donn dans un
contexte d'affichage donne. En effet, la taille du texte dpend du contexte d'affichage car la
modification de la police entrane une modification de la taille occupe par le texte. L'utilisation de
polices personnalises sera vue dans le chapitre suivant.
La fonction suivante dessine un texte en rouge centr dans la zone client de la fentre spcifie.
void DrawCenteredText(HWND hWnd,char *text)
{
HDC hDC;
Page 40 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
RECT rcClient;
GetClientRect(hWnd,&rcClient);
hDC=GetDC(hWnd);
SetTextColor(hDC,0x000000FF);
SetBkMode(hDC,TRANSPARENT);
SetTextAlign(hDC,TA_CENTER|TA_TOP);
TextOut(hDC,(int)((float)rcClient.right/2),5,text,strlen(text));
ReleaseDC(hWnd,hDC);
}
12. Utilisation de polices personnalises
Cours thorique :
Pour utiliser une police personnalise pour l'affichage du texte, il faut tout d'abord crer la
police spcifie. Pour cela, on ralise un appel CreateFont() pour dterminer la police voulue, la
taille, le style... Il faut ensuite slectionner la police dans le contexte d'affichage avec la fonction
SelectObject(). Une fois qu'une police est slectionne dans un contexte d'affichage, chaque texte
affich le sera avec cette police.
SelectObject() retourne le 'handle' de la police prcdemment slectionne. Une fois
l'utilisation de la police termine, l'application doit reslectionner l'ancienne police par un nouvel
appel SelectObject(). Ensuite, elle doit dtruire la police inutilise par un appel DeleteObject().
La suppression des objets crs par une application est faite automatiquement lorsque celle-ci
se termine. Cependant, ils occupent de la place inutilement durant l'excution de l'application. Il est
donc important de supprimer les objets inutiliss. De plus, le nombre d'objets disponible pour une
application donne est limit, si l'application recre une nouvelle police chaque modification de
l'affichage sans supprimer l'ancienne, elle va rapidement atteindre le nombre maximal d'objets.
Voici un exemple d'appel CreateFont() :
HFONT Police;
Police=CreateFont(
20,
0,
0,
0,
700,
FALSE,
FALSE,
FALSE,
0,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"Comic Sans MS"
);
13. Affichage d'une image
Page 41 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Cours thorique :
Avant de pouvoir afficher une image, il faut tout d'abord la charger. Nous ne traiterons ici que
les images bitmap. En effet, ce sont les seules images prises en charge par l'API Windows. Pour
effectuer des traitements sur les images avec des fonctions du GDI (Graphic Device Interface), il faut
donc tout d'abord les convertir au format bitmap. Les images sont considres comme des objets du
GDI, au mme titre que les polices. On peut donc utiliser les fonctions de manipulation d'objet (ex. :
SelectObject() ou DeleteObject()) aussi bien sur des images que sur des polices.
On peut charger une image partir du disque ou encore partir des ressources du programme.
Pour cela, on peut utiliser deux fonctions : LoadBitmap() ou LoadImage(). La fonction
LoadBitmap() est beaucoup moins complte que LoadImage(), il est donc prfrable d'utiliser cette
dernire. Ce n'est cependant pas une obligation. Une fois l'image charge en mmoire, le GDI
retourne un 'handle' (de type HBITMAP) sur l'image. Elle peut alors tre affiche.
Le rsultat de l'affichage dpend bien entendu du contexte d'affichage utilis (en particulier du
nombre de couleurs qu'il comprend). Pour afficher l'image, on utilisera la fonction DrawState().
Cette fonction n'affiche pas ncessairement des bitmaps, elle peut aussi afficher des icnes par
exemple. De plus, cette fonction est capable de modifier la taille d'affichage du bitmap. Il n'est
cependant pas recommand d'agrandir la taille des images l'affichage car l'agrandissement est alors
de trs mauvaise qualit.
Voici un exemple de fonction dessinant une image charge depuis un fichier dans la fentre
spcifie :
void PrintBmp(HWND hWnd, char *filename)
{
HBITMAP hBmp;
HDC hDC;
hBmp=(HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
hDC=GetDC(hWnd);
DrawState(hDC,NULL,NULL,(LPARAM)hBmp,NULL,0,0,0,0,DST_BITMAP);
DeleteObject(hBmp);
ReleaseDC(hWnd,hDC);
}
14. Dessin
Cours thorique :
L'affichages de 'dessins' (lignes, cercles, points) dans une fentre passe par le GDI. Les dessins
se font avec un pointeur. Par dfaut, le pointeur est un pixel unique. Tracer une ligne donne donc le
rsultat attendu : une ligne de 1 pixel de large. Pour dessiner une ligne plus large, il va falloir
remplacer le pointeur courant par un pointeur plus large. Pour cela, on utilise le fonction CreatePen
(). Elle retourne une variable de type HPEN. Ensuite, il faut slectionner le pointeur ainsi cr dans
le contexte d'affichage courant. Bien entendu, il faudra, tout comme avec les polices, supprimer les
Page 42 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
pointeurs ds que ceux-ci ne sont plus utiliss.
Voici un aperu des fonctions utilises pour dessiner :
SetPixel() dessine un pixel unique, indpendamment du pointeur courant.
GetPixel() retourne la couleur du pixel la position spcifie.
MoveToEx() dplace la position courante du curseur de dessin.
LineTo() trace une ligne depuis la position courante du curseur de dessin jusqu' la position
spcifie. La ligne est trace avec le pointeur courant.
Arc(), ArcTo() et AngleArc() dessinent des ellipses ou des portions d'ellipses.
Polyline() dessine une srie de lignes connectes.
15. Fond personnalis
Cours thorique :
Jusqu' prsent, nous avions laiss Windows effacer le fond. L'effacement de la zone client
avant le redessinement n'est pas ncessaire, la mthode la plus simple et la plus rapide est de
redessiner par dessus l'ancienne zone. Toutefois, si l'on veut redessiner une partie de la zone client
seulement ou si le nouveau dessin ne couvre pas toute la zone (du texte avec fond transparent par
exemple) il est ncessaire d'effacer d'abord.
Pour cela, il faut utiliser le message WM_ERASEBKGND. Lorsque ce message est envoy,
un 'handle' sur le contexte d'affichage effacer est pass en paramtre. Il faut imprativement utiliser
le 'handle' pass en paramtre pour effectuer toutes les oprations de dessin. L'application dispose de
plusieurs solutions pour effacer la zone client. Elle peut effectuer des tches complexes, afficher des
images, puis un texte... Si les images ne couvrent pas toutes la zone, il faut effacer les parties qui
resteront visibles. Pour cela, on peut utiliser la fonction FillRect(). Cette fonction ne prend pas en
paramtre une couleur mais un HBRUSH. On peut crer des 'brushes' partir d'images bitmap avec
CreatePatternBrush() ou encore partir d'une couleur unie avec CreateSolidBrush(). Si
l'application dsire simplement modifier la couleur de fond de sa zone client, elle n'est pas oblige de
traiter le message WM_ERASEBKGND. Elle peut simplement utiliser CreateSolidBrush() et
passer le rsultat au membre hbrBackground de la structure WNDCLASSEX lors de la cration de
la classe de la fentre. De cette manire la fonction DefWindowProc() crasera le fond elle mme
avec la couleur demande.
16. Commandes systmes
Cours thorique :
Pour personnaliser une application, on peut tre amen modifier le traitement standard des
commandes systmes. Pour cela, on utilise le message WM_SYSCOMMAND. Ce message est
Page 43 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
envoy en diverses occasions : dplacement de la fentre, utilisation du menu de contrle sur la
barre de titre (fermer, minimiser, restaurer), activation de l'cran de veille, droulement du menu
dmarrer... En interceptant ce message, une application peut dfinir des ractions personnalises aux
traitements standard Windows. Par exemple, masquer la fentre au lieu de la dtruire lors d'un clic
sur la 'croix'. Comme le mme message est envoy (WM_SYSCOMMAND) quel que soit la
commande systme, il faut imprativement appeler DefWindowProc() pour traiter les commandes
systmes non prises en charge par l'application.
17. Menus
Cours thorique :
Il existe deux manires d'insrer des menus dans une application. La premire mthode est de
crer un menu grce un diteur de ressources puis de l'insrer dans le programme. La seconde
mthode est dynamique, grce aux fonctionnalits de l'API Windows. De manire gnrale, les
menus obtenus par des ressources seront utiliss pour les menus principaux des fentres, pour crer
des barres de menus. Pour les menus contextuels, botes outils, ..., il est prfrable d'utiliser des
menus dynamiques. A chaque clic dans un des menus, la fentre possdant le menu reoit un
message WM_COMMAND indiquant l'identifiant de l'option choisie.
Pour obtenir une barre de menus partir d'un fichier de ressources, il suffit de crer le menu
dans l'diteur de ressources puis de passer son identifiant au membre lpszMenuName de la structure
WNDCLASSEX. Le menu sera insr automatiquement dans la fentre.
Pour un menu dynamique, on utilisera l'API Windows. Les menus sont identifis par une
variable HMENU. Il faut tout d'abord crer le menu. Il sera initialement vide. Pour cela, il faut
utiliser la fonction CreatePopupMenu(). Une fois le menu cr, on peut lui ajouter des lments
(options, sous menus, sparateurs) avec les fonctions AppendMenu() ou InsertMenuItem(). Une
fois le menu cr, il faut le dessiner. Pour cela, on utilise les fonctions TrackPopupMenu() ou
TrackPopupMenuEx(). Pour une bote outils, il suffira de dessiner le menu la position courante
obtenue par un appel GetCursorPos(). Si le menu est associ une fentre, il sera supprim
automatiquement lors de la destruction de la fentre. Dans le cas contraire, l'application doit appeler
la fonction DestroyMenu() pour librer les ressources.
La fonction suivante dessine un menu contextuel :
void PrintMenu(HWND hWnd)
{
HMENU hMenu;
POINT pt;
GetCursorPos(&pt);
hMenu=CreatePopupMenu();
AppendMenu(hMenu,MF_STRING,1,"Item 1");
AppendMenu(hMenu,MF_STRING,2,"Item 2");
AppendMenu(hMenu,MF_SEPARATOR,NULL,NULL);
AppendMenu(hMenu,MF_STRING,3,"Item 3");
TrackPopupMenu(hMenu,NULL,pt.x,pt.y,0,hWnd,NULL);
Page 44 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
}
Dans cet exemple, les valeurs 1, 2 et 3 passes AppendMenu() reprsentent les identifiants
des options. Ces valeurs seront passes en paramtre lors de l'envoi du message WM_COMMAND.
Elles n'ont aucune signification particulire, si ce n'est pour le programmeur lui mme.
18. Cration dynamique d'un contrle
Cours thorique :
La cration dynamique de contrles ne prsente aucune difficult particulire. Elle se fait grce
la fonction CreateWindowEx(), en utilisant des classes de fentres prdfinies. Une fois le
contrle cr, il faut tout de mme remarquer que son utilisation est quelque peu diffrente. En effet,
des fonctions telles que SetDlgItemText() ne fonctionneront plus. Il faudra utiliser la fonction
SetWindowText() la place, puisque le contrle est une fentre. La fonction SetDlgItemText()
n'est en fait qu'un raccourci qui appelle SetWindowText() aprs avoir utilis GetDlgItem() pour
obtenir le 'handle' du contrle correspondant.
Les styles utiliss dans l'diteur de ressources sont les mmes. La fonction CreateWindowEx
() acceptera donc comme styles l'ensemble des styles utiliss dans l'diteur de ressources.
Les classes suivantes sont prdfinies : BUTTON, COMBOBOX, EDIT, LISTBOX,
MDICLIENT, RichEdit, RICHEDIT_CLASS, SCROLLBAR, STATIC.
Chapitre 4
Le systme de fichier
1. Introduction
Cours thorique :
L'API Windows fournit une suite de fonctions permettant d'accder au systme de fichier. Bien
entendu, il est toujours possible d'utiliser les fonctions classiques du C (fopen(), fprintf()) pour
accder au systme de fichier. Cependant, ces fonctions ne supportent pas toutes les possibilits du
systme de fichier Windows (ex. : la gestion du partage des ressources dans l'environnement
Windows). Il est donc en gnral prfrable d'utiliser les fonctions fournies avec l'API Windows
pour manipuler le systme de fichier.
Les fonctions de manipulation de fichiers (ouverture, criture, lecture) seront donc abordes
dans ce paragraphe. De plus, l'utilisation de fichiers dans un contexte multi-tches impose des
prcautions d'emploi supplmentaires. Le non respect de certaines prcautions simples peut entrainer
des erreurs ou mme la perte ou l'crasement de donnes.
Page 45 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Le problme de la recherche de fichiers dans le systme de fichier sera galement abord.
Ce paragraphe a donc pour but de prsenter de manire gnrale les oprations de base lors de
la manipulation du systme de fichier Windows grce aux fonctions de l'API Windows.
2. Cration et ouverture d'un fichier
Cours thorique :
L'API Windows fournit la fonction CreateFile() pour ouvrir ou crer des fichiers. Cette
fonction permet de grer le partage du fichier au niveau systme, les attributs de ce fichiers. Il est
important de bien comprendre les paramtres principaux de cette fonction.
Avant la description plus en dtail de cette fonction, il est important de bien comprendre le
fonctionnement du systme de fichiers sous Windows. Comme cet environnement est multi-tches, il
est possible qu'un fichier soit accd en mme temps par plusieurs programmes ou mme par le
mme programme. Le premier programme demander l'accs au fichier doit prciser si il souhaite
ou non le partager. Si le fichier n'est pas partag, le systme refusera tout autre appel CreateFile()
pour accder ce fichier. Le fichier ne sera donc plus accessible au reste du systme. Le fichier
restera 'rserv' jusqu' ce que le programme se termine ou jusqu' ce que la fonction CloseHandle()
soit appele (cette dernire fonction sera explique plus tard). En ne partageant pas le fichier le
programme s'assure qu'il est le seul accder au fichier.
Un fichier peut galement tre partag en lecture. Dans ce cas, seul le programme ayant accd
au fichier en premier aura un accs en lecture/criture. Les appels suivants CreateFile()
permetteront seulement d'accder au fichier en lecture.
Si le fichier est partag en lecture/criture, alors l'accs simultan en lecture ou criture ce
fichier est autoris. Plusieurs programmes peuvent donc lire ou crire des donnes simultanment
dans ce fichier. Ce dernier mode de partage est trs dangereux et ne doit tre utilis qu'en
connaissance de cause.
Lors du premier appel CreateFile(), le mode de partage du fichier est dfini. Il ne pourra pas
tre modifi par un autre appel. Mme si le fichier est partag, son mode de partage ne pourra pas
tre modifi. Si l'appel la fonction CreateFile() russit, le systme retourne un HANDLE au
programme. Ce HANDLE identifie le fichier. La fonction CloseHandle() permet de refermer le
fichier. A partir de cet appel, le HANDLE identifiant le fichier n'est plus valide.
L'identifiant du fichier stocke galement la position courante du pointeur. Le systme stockera
donc la position courante d'criture pour chaque nouvel appel CreateFile(). Si le fichier est ouvert
10 fois simultanment, le systme stockera 10 positions. Le partage du fichier n'entrane donc
aucune modification dans les mthodes de lecture/criture sur le fichier (le pointeur courant ne sera
pas modifi mme si un autre programme accde le fichier). Cependant, si deux threads utilisent le
mme HANDLE pour accder un fichier, alors le pointeur pourra tre modifi par l'un ou par
l'autre. Si le mme thread fait deux appels successifs l'API pour lire le fichier, rien ne garantit qu'il
Page 46 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
lira deux blocs successifs. En effet, si le deuxime thread a modifi le pointeur entre les deux
appels, alors la lecture se poursuivra la nouvelle position.
Pour viter des complications, le plus simple est gnralement de ne pas partager les fichiers
ou d'autoriser seulement le partage en lecture.
Voici un dtail de principaux arguments de la fonction CreateFile() :
HANDLE CreateFile(
LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
// pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to
// copy
);
lpFileName est le nom du fichier ouvrir ou crer.
DwDesiredAccess est le type d'accs demand : lecture (GENERIC_READ), criture
(GENERIC_WRITE) ou les deux. Si le fichier est partag en lecture seulement et qu'un accs en
criture est demand, la fonction retournera une erreur.
DwShareMode indique le mode de partage demand. Ce mode peut tre non partag (NULL),
partag en lecture (FILE_SHARE_READ), partag en criture (FILE_SHARE_WRITE) ou les
deux.
DwCreationDisposition indique la mthode utilise pour lire ou crer le fichier.
CREATE_NEW demande la cration d'un fichier. Si le fichier existe dj, la fonction retournera une
erreur. CREATE_ALWAYS demande la cration d'un fichier. Si le fichier existe dj, il est cras.
OPEN_EXISTING demande l'ouverture d'un fichier existant. Si le fichier n'existe pas, le fonction
retourne une erreur. PEN_ALWAYS demande l'ouverture du fichier. Si le fichier n'existe pas il est
cr.
dwFlagsAndAttributes indique les attributs utiliss pour crer le fichier.
La fonction retournera un HANDLE en cas de succs ou INVALID_HANDLE_VALUE,
c'est dire le pointeur 0xFFFFFFFF est cas d'erreur.
Ds que le fichier n'est plus utilis, l'application peut utiliser CloseHandle() pour signifier
qu'elle n'utilise plus le fichier spcifi. Toute autre application pourra alors avoir accs ce fichier si
elle le demande.
3. Lecture/Ecriture dans un fichier
Cours thorique :
Avant de pouvoir lire ou crire dans un fichier, il faut raliser un appel CreateFile(). Le
HANDLE retourn permettra d'identifier le fichier dans lequel on souhaite effectuer les oprations.
Page 47 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
La fonction ReadFile() permet la lecture dans un fichier. Elle dplace le pointeur courant la
nouvelle position. ReadFile() lit le nombre d'octets spcifis et retourne un indicateur boolen, ainsi
que le nombre d'octets lus. ReadFile() retournera toujours TRUE si le pointeur sur le fichier est
valide. Pour dtecter la fin du fichier, il faut comparer le nombre d'octets lus au nombre d'octets
demands. Si 256 octets sont demands la lecture et que ReadFile() indique que seulement 18
octets ont t lus, alors la fin du ficher est atteinte.
La fonction WriteFile() a un mode de fonctionnement similaire ReadFile(). Elle crit le
nombre d'octets demand, retourne un indicateur de succs ainsi que le nombre d'octets
effectivement crits.
Pour dplacer le pointeur courant on utilise la fonction SetFilePointeur(). La nouvelle position
peut tre spcifie partir du dbut du fichier, de la position courante du fichier ou de la fin du
fichier. Cette fonction retourne la nouvelle position. Pour obtenir la position courante du pointeur, on
appelle SetFilePointeur() en demandant un dplacement nul partir de la position courant. On
rcupre ainsi la position courante du pointeur.
La fonction SetEndOfFile() dtermine la position de la fin du fichier. La fin du fichier est
alors place la position courante du pointeur.
4. Manipulations sur les fichiers
Cours thorique :
D'autres fonctions peuvent tre utilises lors de la manipulations de fichiers. Pour obtenir la
taille d'un fichier, il faut utiliser GetFileSize(). Cette fonction retourne sur 32 ou 64 bits la taille du
fichier spcifi.
Les fonctions MoveFileEx(), CopyFileEx() et DeleteFile() peuvent tre utilises pour les
manipulations courantes sur les fichiers. Ces fonctions ne ncessitent pas que le fichier soit
prcdemment ouvert par CreateFile(). La fonciton DeleteFile() supprime un fichier sans le faire
passer par la corbeille. Il est donc impossible d'annuler la suppression du fichier. Pour envoyer
simplement un fichier dans la corbeille, il faut utiliser la fonction SHFileOperation(). Cette fonction
permet la fois la suppression et l'envoi la corbeille d'un fichier.
Le systme de fichier VFAT (utilis sous Windows 95/98) manipule des noms courts et des
noms longs. Les noms de fichiers courts (format 8.3) sont un hritage des prcdents systmes de
fichiers utiliss. Pour convertir des nom de fichiers en version courte ou longue on utilise les
fonctions GetShortPathName() et GetFullPathName().
5. Enumration de fichiers
Cours thorique :
Page 48 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Pour numrer des fichiers, l'API Windows offre une slection de 3 foncitons : FindFirstFile
(), FindNextFile() et FindClose(). La fonction FindFirstFile() initialise la recherche et retourne le
premier fichier trouv (si il y en a un). Elle retourne un HANDLE sur la recherche en cours, pour
permettre de poursuivre la recherche. Le fonction FindNextFile() doit ensuite tre appele pour
rcuprer l'ensemble des fichiers trouvs. Gnralement cette fonction sera appele dans une boucle.
La fonction FindClose() referme le HANDLE et libre la mmoire occupe.
Cette suite de fonctions permet d'effectuer des recherches dans un dossier. Attention, les sous
dossiers ne seront pas parcourus. La recherche peut porter sur un listage non exhaustif, par exemple,
listage des fichiers '*.txt'. Pour un listage exhaustif, il faudra demander le listage des fichiers '*.*'. Le
type de recherche, ainsi que le dossier de recherche sont passs la fonction FindFirstFile() sous
forme d'une chane de caractre de type : Chemin\\Masque (ex. c:\*.*). Aucune fonction n'est fournie
pour le parcours rcursif des dossiers de manire explorer les dossiers et sous dossiers. Pour
effectuer une recherche complte, il faut effectuer des appels successifs ces 3 fonctions de manire
parcourir l'ensemble des sous dossiers. Lors d'une recherche, les pseudo-dossiers '.' et '..' sont
galement lists. Il peut donc tre utile d'effectuer un test pour supprimer ces dossiers du listage
final. Les informations concernant les fichiers et dossiers lists sont places dans une structure
WIN32_FIND_DATA (attributs, nom, taille...).
6. Manipulations sur les dossiers
Cours thorique :
L'API Windows offre peu de manipulations possibles sur les dossiers. La fonction
CreateDirectory() permet la cration d'un dossier. Attention toutefois au fait que cette fonction ne
cre pas les sous dossiers ncessaires. Si la cration du dossier demand demande la cration
implicite de plusieurs dossiers, la fonction retournera une erreur. Pour ex., le dossier c:\a\b ne sera
cr que si le dossier c:\a existait dj. Pour crer ce dossier, il faudra faire deux appels
CreateDirectory() pour crer respectivement les dossiers c:\a et c:\a\b.
Pour supprimer un dossier, on utilisera la fonction RemoveDirectory(). Cette fonction ne
supprimera le dossier que si il est vide. Pour supprimer un dossier non vide il faudra donc lister
l'ensemble des fichiers et dossiers et les supprimer avant de supprimer le dossier parent.
La fonction GetCurrentDirectory() retourne le rpertoire courant. Elle peut tre utilise par
exemple pour obtenir le chemin du processus courant. En effet, gnralement, les programmes sont
excuts avec pour dossier courant, le dossier contenant l'excutable. Ceci est toujours vrai sauf
mention contraire de l'utilisateur. La rcupration du chemin de l'excutable courant par cette
mthode n'est donc pas garantie mais elle marchera dans la plupart des cas.
La fonction GetTempPath() permet de rcuprer le chemin du dossier temporaire
(gnralement Windows\temp). Les fonctions GetWindowsDirectory() et GetSystemDirectory()
permettent de rcuprer le chemin du dossier contenant Windows et du dossier systme (system ou
Page 49 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
system32).
7. Manipulations des chemins
Cours thorique :
Windows fournit une collection de fonctions permettant de travailler sur les fichiers, les
dossiers ou les chemins. Voici une liste non exhaustive des fonctions les plus utilises :
PathIsDirectory() permet de dterminer si le chemin spcifi est un dossier.
PathFileExist() dtermine si le fichier spcifi existe.
PathCanonicalize() supprimer toutes les rfrences symboliques du chemin et le convertit en
chemin absolu. Ex. : (c:\a\.. sera converti en c:\).
PathCompactPath() permet de rduire la taille occupe par le chemin. Cette fonction
remplace une partie de chemin par des points.
PathFindExtension() retourne l'extension du nom de fichier spcifi.
PathFindFileName() retourne le nom de fichier dans le chemin spcifi.
PathGetArgs() retourne les arguments du chemin spcifi.
PathRelativePathTo() permet de relativiser un chemin par rapport un autre chemin.
PathRemoveArgs() supprime les arguments du chemin pass en paramtre.
PathRemoveExtension() supprime l'extension du chemin, si celle-ci existe.
Pour une liste exhaustive, reportez vous l'annexe B.
Chapitre 5
Le multithreading
1. Introduction
Cours thorique :
Commenons tout d'abord par une explication sur le terme de multithreading. Le
multithreading est le fait d'excuter des tches simultanment (multitche). Les termes multitche et
multithread ne sont pas totalement similaires. Le terme multithread indique une application qui
effectue diffrentes taches simultanment. Le terme de multitche est un terme plus gnral et plus
commun pour parler d'un systme capable de grer plusieurs applications simultanment.
Dans un programme classique, les instructions sont traites de manire linaire, "ligne ligne".
En ignorant le reste du systme on pourrait dire que le processeur excute les instructions de la ligne
courante, puis passe la suivante. En ralit ce n'est pas tout fait vrai car les autres applications
Page 50 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
doivent elles aussi avoir accs au processeur, ainsi que le systme d'exploitation lui mme.
Un thread est en fait une unit d'excution. Chaque thread peut avoir accs au processeur et
excuter des instructions. Un thread peut se comporter exactement comme si il ne partageait pas le
processeur (mais dans ce cas, il faudrait disposer d'autant de processeurs qu'il y a de threads). Pour
viter cet inconvnient, le systme donne l'accs au processeur chaque thread durant un temps trs
court (quelques millisecondes), et ceci de manire circulaire. Si le nombre de thread augmente,
chaque thread devra attendre un dlai plus long avant d'obtenir de nouveau l'accs au processeur.
De cette manire, chaque application peut fonctionner comme si elle tait seule utiliser le
processeur. La seule diffrence est que l'excution est plus lente. Si le processeur est suffisamment
rapide, l'excution d'un nombre modr de threads peut s'effectuer trs rapidement. C'est le cas
actuellement. Dans un systme utilisant Windows, le nombre de threads avoisine 50 au repos. Il
dpasse rapidement 100.
L'utilisation du multithread consiste crer un programme qui comporte plusieurs threads. Ce
programme peut donc excuter plusieurs instructions "simultanment". On peut alors se demander
l'utilit d'une telle pratique. En effet, puisque le processeur est partag, l'excution du programme ne
sera pas plus rapide. Elle sera seulement fragmente. Un exemple simple permet de comprendre
l'utilit du multithreading.
Considrons une application ralisant un transfert de fichier. Cette application dispose d'une
interface graphique prsentant une barre de progression ainsi qu'un bouton 'annuler'. En effet
l'utilisateur dsire tre tenu au courant de l'avancement de la copie. De plus il veut pouvoir stopper
celle ci tout moment si il le dsire. Le programme doit donc raliser une boucle de manire
recevoir les messages. Mais il doit galement s'occuper de la copie des fichiers, ce qui reste son rle
principal. Si le programme crit sur le disque, il ne peut plus recevoir les messages. La fentre ne
sera donc plus rafrachie. De plus, si l'utilisateur clique 'annuler', l'application ne traitera pas cette
demande puisqu'elle ne recevra mme pas le message... Une telle application utilisera donc 2 threads.
Le premier thread s'occupera de la rception et du traitement des messages. Le deuxime thread
s'occupera de la copie des fichiers. De cette manire, la rception des messages sera effectue mme
au cours de la copie. Si l'utilisateur demande l'arrt de la copie, le premier thread devra simplement
stopper l'excution du second. La copie sera alors stoppe.
2. Notion de processus et de thread
Cours thorique :
Il ne faut pas confondre les processus et les threads. Un processus est une application. Tout
processus dispose d'au moins un thread. Il peut cependant en possder plusieurs. Pour l'utilisateur
moyen, les threads sont invisibles. Ils sont masqus par les processus. La diffrence entre un
processus et un thread est qu'un processus ne constitue en rien une unit d'excution. L'excution
simultane de plusieurs processus est ralise grce au multithreading. Le processus reprsente
Page 51 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
simplement un application vis vis du systme. Lorsque l'utilisateur demande la terminaison
d'une application, tous les threads appartenant cette application devront tre termins.
Tout processus doit comporter au moins un thread, de manire excuter le point d'entre du
programme. Ce thread ne pourra jamais tre termin sans que l'application ne soit elle mme
termine. Le processus ne doit en aucun cas tre assimil ce thread. Le thread constitue simplement
l'unit d'excution de base du processus. Le systme ne communiquera jamais avec le processus mais
toujours avec l'un des threads de ce processus. En effet, le processus n'tant pas une unit d'excution
il ne ralise aucune tche. Le premier thread est cr par le systme. C'est pour cette raison que dans
un programme comportant un seul thread, aucune rfrence n'est faite aux fonctions de l'API
relatives aux threads. La cration de nouveaux threads devra tre explicite. C'est ce qui sera tudi
tout au long de ce chapitre.
3. Partage des tches
Cours thorique :
Avant d'utiliser des threads, il est ncessaire de bien comprendre comment le systme gre ces
threads. En effet, un programme multithread mal conu peut conduire une trs forte baisse des
performances systme.
Il faut bien comprendre que si le systme d'exploitation accorde chaque thread un temps
d'accs au processeur, le thread n'est pas oblig d'utiliser totalement ce temps. Le systme accorde
simplement un temps maximum d'utilisation. Au del de cette priode, le contrle du processeur sera
pass un autre thread. Si le thread n'a plus aucune tche effectuer, il doit repasser lui mme le
contrle au systme. De cette manire, une application inactive ne monopolisera pas inutilement le
processeur. Si une application inactive n'effectue pas ce passage, elle utilisera totalement son quota
d'accs. Le systme affichera alors une charge processeur de 100% et les performances seront
fortement ralenties.
Le systme d'exploitation dispose galement d'un systme de priorits permettant de grer les
quotas d'accs allous aux processus. Voici un rsum rapide du fonctionnement de ce systme de
priorit :
Time Critical : ce thread requiert l'utilisation totale du processeur. Les quotas allous ce
thread sont infinis. Si un tel thread ne repasse pas spontanment le contrle au reste du systme, le
systme restera en attente. Le contrle du processeur ne sera accord aucun autre thread. Tout le
systme sera alors bloqu jusqu' ce que le thread repasse le contrle. Ce type d'application n'est en
ralit quasi jamais utilis. Il doit tre rserv des utilisations trs particulires (ex : un gestionnaire
de souris).
High : ce thread recevra le contrle du processeur en priorit. Un tel thread ne peut pas tre
bloquant pour le systme mais il pourra ralentir normment les performances. Un thread utilisant
cette priorit et effectuant un accs permanent au processeur causera un effondrement des
Page 52 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
performances gnrales du systme. Cette priorit ne doit gnralement tre accord qu' des
applications effectuant des tches trs brves ou tant en premier plan. Les performances de ce
thread ne seront quasiment pas affectes par l'excution d'autres applications.
Normal : ce thread a une priorit normale. Il recevra le processeur exactement autant de fois
que les autres threads de la mme priorit. Si deux threads utilisent cette priorit et utilisent le
processeur en permanence, les performances de chacun des thread seront divises par 2. La quasi
totalit des threads utilise cette priorit (c'est d'ailleurs la plus recommande hors cas trs
particuliers).
Idle : ce thread recevra le contrle du processeur seulement si aucune autre application ne
demande le contrle. Si ce thread effectue des tches en continu et que toutes les autres applications
sont inactives, il disposera de l'accs processeur dans sa quasi totalit. Si un autre thread effectue des
tches continues, ce thread n'aura plus aucun accs au processeur. Ce type de priorit peut tre
accord des applications effectuant des tches de fond. De cette manire les performances du
systme resteront inchanges. La charge du processeur sera alors continuellement de 100%. Le
partage sera alors : 0% pour le thread Idle si le systme utilise le processeur. 100% pour le thread
Idle si le systme est inactif.
De manire gnrale il est inutile de modifier la priorit des threads. La modification des
priorits ne doit tre faite que dans les cas particuliers et en connaissance de cause. En particulier, les
priorits 'Time Critical' et 'High' ne doivent jamais tre utilises abusivement.
De manire conserver des performances systme optimales, un thread doit repasser le
contrle au systme ds qu'il devient inactif. Pour cela il dispose de plusieurs mthodes. Prenons
l'exemple d'un thread qui dsire attendre 150ms. Si ce thread effectue une boucle en testant l'heure
systme, il utilisera totalement son quota et le systme affichera une charge de 100%. Si eu lieu de
cela le thread effectue un appel la fonction Sleep(), il repassera le contrle au reste du systme pour
une dure de 150ms. Le contrle du processeur ne lui sera plus accord durant cette dure. La charge
processeur sera alors de 0%. Le thread sera alors en attente. De cette manire les autres threads
auront accs au processeur comme ci ce thread n'tait pas prsent et les performances systme seront
conserves.
Un autre exemple est la rcupration des messages. La fonction GetMessage() repasse le
contrle au systme ds que tous les messages ont t traits. Si l'application est inactive, le contrle
est donc repass immdiatement au systme et la charge processeur affiche est de 0%. Si un thread
utilise PeekMessage() dans une boucle, le thread conservera l'accs au processeur mme si aucun
message n'est prsent dans la file d'attente. La charge processeur sera donc de 100% mme si
l'application est inactive. Ce type d'erreurs ne doit donc surtout pas tre commis sous peine de
diminuer gravement les performances globales du systme.
Il est galement indispensable de comprendre qu'on ne peut jamais prvoir l'ordre dans lequel
les threads recevront l'accs au processeur. Une application multithread devra toujours utiliser les
fonctions de synchronisation (qui seront dtailles plus tard) et ne devra jamais tenter de prvoir
l'ordre d'excution des instructions. De plus, deux lignes conscutives d'une fonction ne seront pas
Page 53 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
forcment effectues la suite car le contrle du processeur pourra tre pass d'autres threads
entre temps. Il faut donc bien avoir en tte les contraintes imposes par un programme multithread
avant de se lancer dans sa ralisation.
4. Synchronisation
Cours thorique :
Les fonctions de synchronisation sont indispensables dans un programme multithread. La
mauvaise synchronisation d'une application peut conduire des plantages ou des performances
totalement dsastreuses.
Etudions l'exemple d'un processus contenant deux threads, chacun utilisant le mme tableau
global. Un tel programme peut donner des rsultats surprenant dans le cas d'une mauvaise
synchronisation. Par exemple, deux valuations conscutives de la mme variable pourront retourner
des valeurs diffrentes. Ce genre de comportement n'est gnralement pas souhait. Les deux threads
devront alors se synchroniser avant d'accder aux ressources partages. Dans ce cas, la
synchronisation consistera en une exclusion mutuelle (d'autres types de synchronisation seront
tudies plus tard). L'exclusion mutuelle consiste faire patienter tous les threads demandant l'accs
aux donnes partages tant que le thread les utilisant n'a pas dclar qu'il avait termin. Chaque
thread utilise une fonction pour signaler qu'il entre dans une section protge. Il utilisera ensuite une
seconde fonction pour signaler qu'il sort de la section protge. Si un autre thread demande entrer
dans un bloc protg, il sera mis en attente. Le thread qui est entr dans la section protge doit
imprativement signaler sa sortie de la section protge sinon les autres threads resteront en attente.
L'accs aux donnes partages doit donc tre aussi bref que possible.
Les fonctions de synchronisation sont un trs bon moyen de mettre des threads en attente tout
en conservant une charge processeur nulle. Si un thread entre dans une phase d'attente, son utilisation
processeur restera trs faible durant toute la priode d'attente. Le systme lui accordera de nouveau le
contrle ds que cela sera possible.
La synchronisation des threads devra donc toujours tre effectue par des fonctions de l'API et
jamais par des boucles. L'utilisation de boucles ne repassant pas le contrle au systme rendrait les
performances dsastreuses et ralentirait l'excution du processus lui mme car l'accs du processeur
serait accord des threads en attente, ce qui ne prsente aucun intrt.
5. Premier exemple
Cours thorique :
Ce premier exemple montre comment crer un thread. Le point d'entre du thread peut tre
n'importe quel fonction respectant un certain typage. Ds la fonction constituant le point d'entre
retourne, le thread est termin par le systme.
Page 54 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
La fonction CreateThread() qui dmarre le thread retourne un HANDLE ainsi qu'un
identifiant. L'identifiant est une valeur de type DWORD. Dans cet exemple, le HANDLE et
l'identifiant ne nous seront d'aucune utilit car le thread se terminera spontanment. Le HANDLE ne
sera donc mme pas rcupr. Il est indispensable de passer un pointeur non nul pour l'identifiant.
Dans le cas contraire, la fonction CreateThread() chouera. L'identifiant ou le HANDLE sont
utiliss pour communiquer avec le thread.
Notons que l'ensemble des variables globales est accessible la procdure du thread.
Tlcharger le projet comment : Projet 08
6. Arrt d'un thread
Cours thorique :
Le problme de l'arrt d'un thread est dlicat. Un thread peut s'arrter spontanment en
terminant sa fonction principale. Dans ce cas, on considrera que l'arrt tait prvu. Dans le cas o
l'application doit se terminer, l'arrt ne peut pas tre prvu. Dans ce cas, il existe diffrentes
mthodes de terminer les threads en cours.
Voici tout d'abord la mthode la plus vidente, et aussi la plus mauvaise. La fonction
TerminateThread() fournie par l'API permet de terminer un thread. Le thread est alors
immdiatement stopp. Bien que cette fonction soit fournie en standard avec l'API Windows, elle ne
sera en pratique quasiment jamais utilise. Son utilisation doit tre rserve des cas trs particuliers
et en connaissance de cause. Comme ces cas sont gnralement trs rares on peut considrer que
dans un programme bien conu, cette fonction ne devra jamais tre utilise. En effet, cette fonction
termine le thread courant sans appeler les fonctions de dsallocation de mmoire. Ceci est d au fait
qu'il n'existe pas de point de sortie prdfini un thread. La fonction TerminateThread() ne peut
donc pas effectuer la dsallocation de mmoire. Toute la mmoire alloue de manire dynamique
sera donc perdue. De mme, les dlls charges par le thread ne seront pas libres... Cette fonction ne
doit donc tre utilise que dans le cas d'un thread n'utilisant que des allocations de mmoire statique,
et aucune ressource extrieure...
Pour viter l'utilisation de cette fonction, chaque thread doit tre muni d'un ou de plusieurs
points de sortie. Les systmes de communication inter-threads seront utiliss pour mener le thread
vers ce point de sortie. De cette manire la dsallocation de la mmoire ainsi que la libration des
ressources extrieures pourra tre effectue. La communication inter-threads sera traite plus tard.
On peut tout de mme citer les variables globales et les messages qui sont les principaux moyens de
communication entre threads. Bien que plus complexes mettre en place, les mthodes de
communication inter-threads seront donc toujours prfrer.
L'arrt de l'application entrane automatiquement l'arrt de l'ensemble des threads. Le systme
appellera alors la fonction TerminateThread() pour arrter les threads encore actifs. Une
application doit donc toujours s'assurer de l'arrt de l'ensemble des threads avant de se terminer. Les
Page 55 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
fonctions de synchronisation sont trs adaptes ce type de tche, comme la fonction
WaitForMultipleObjects() qui sera vue en dtails plus tard.
7. Rcupration des messages
Cours thorique :
La rcupration des messages peut avoir deux fonctions : rcuprer les messages de
communication lis une fentre ou rcuprer les messages de communication inter-thread. La
rception des messages doit tre effectue de manire diffrente selon le type de messages. De
manire gnrale, les messages associs des fentres doivent tre traits avec la fonction
GetMessage() dans une boucle. Cette fonction permet de passer le contrle aux autres threads du
systme ds que la file d'attente est vide. De cette manire les performances sont optimales. De plus,
les messages associs aux fentres doivent tre traits rapidement. L'utilisation de cette fonction dans
une boucle permet donc un traitement rapide tout en conservant de bonnes performances.
Le traitement des messages dans le cadre de la communication inter-thread est diffrent (il sera
vu plus en dtail plus tard). Il concerne par exemple l'envoi d'un message d'abandon de la tche
courante. La fonction GetMessage() est trs peu adapte au traitement de ce type de messages car
elle est bloquante. Le thread sera donc suspendu tant que la file d'attente sera vide. Un thread ne
s'occupant pas de la rception des messages associs une fentre a gnralement une tche bien
prcise excuter. Il serait donc absurde d'utiliser une fonction bloquante pour le traitement des
messages. De plus, ces messages ne ncessitent pas un traitement rapide. On prfrera donc utiliser
la fonction PeekMessage() qui elle n'est pas bloquante. Il faudra donc utiliser cette fonction dans
une boucle. Comme l'utilisation de cette fonction dans une boucle provoque de graves problmes de
performances, il ne faudra pas effectuer cette boucle trop frquemment. Prenons l'exemple d'un
thread grant un transfert de fichier. Le transfert de fichier est ralis par une boucle qui lit les
donnes puis les crit une place diffrente. Selon la dure moyenne d'une boucle, on dterminera
un intervalle d'appel de PeekMessage(). Par exemple, toutes les 100 itrations de la boucle
principale, la fonction PeekMessage() sera appele dans une boucle jusqu' puisement de la fille
d'attente. Ce type de traitement est adapt un faible nombre de messages, de manire ne pas
ralentir le travail de la boucle principale. En supposant que la dure moyenne d'une itration de la
boucle soit de 5ms, les messages seront donc traits 2 fois par seconde. Si un message demandant
l'arrt de la tche en cours est envoy, il sera trait au plus 500ms plus tard, ce qui est trs
acceptable. De plus, cette technique ne nuit que trs peu aux performances de la boucle principale
puisque seulement une itration sur 100 sera sensiblement ralentie.
Si la rapidit du traitement des messages n'a que peu d'importance on pourra espacer les appels
PeekMessage() de manire conserver autant que possibles les performances de la boucle
principale. Cette fonction sera donc particulirement adapte au traitement de messages de
notifications envoys des thread effectuant une tche prcise.
Dans le cas d'un thread effectuant des tches 'sur commande' on prfrera bien entendu la
Page 56 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
fonction bloquante GetMessage() puisque le thread sera en attente de tche (et ne consommera
donc pas de ressources).
8. Communication inter-threads
Cours thorique :
La communication inter-threads permet aux diffrent threads d'une application de dialoguer
entre eux. Comme la rception des messages peut poser problme, on tentera gnralement de
rduire au minimum la communication inter-threads et donc de raliser des threads aussi
indpendants que possible.
Outre les fonctions de synchronisation qui ne sont pas proprement parler des fonctions de
communication, il existe deux moyens principaux de communication : les variables globales et les
messages. Les variables globales sont un moyen de communication facile mettre en place.
Toutefois, leur utilisation devra tre faite avec rigueur pour viter des problmes dus l'accs de
variables partages. De plus dans certains cas, l'utilisation de variables globales peut nuire aux
performances.
Une variable globale ne doit jamais tre utilise comme moyen de synchronisation. En effet, il
est possible de placer la valeur d'une variable globale 0 et d'effectuer une boucle avec un test sur
cette variable de manire attendre tant que la valeur reste 0. Un second thread peut alors
dbloquer l'attente en modifiant la valeur de cette variable globale. Bien que fonctionnant, cette
mthode ne devra jamais tre utilise car elle provoque une forte charge processeur inutile. Dans ce
cas, les fonctions de synchronisation devront tre utilises car elles permettent de laisser l'accs
processeur au reste du systme tant que le thread est en attente.
De plus, deux threads ne doivent jamais accder en criture une mme variable globale. Ce
type d'accs peut provoquer des plantages dus la manire dont les variables globales sont utilises
(ces variables sont parfois places dans des registres pour optimisation). L'utilisation de variables
globales pour la communication inter-threads implique une communication sens unique. Un thread
accde cette variable en criture et les autres threads ragissent en fonction de cette valeur. Une
telle utilisation ne posera pas de problmes. Le partage de donnes mmoire en criture implique des
prcautions particulires comme la synchronisation.
Les variables globales pourront donc tre utilises comme drapeau. Par exemple pour signifier
un thread qu'il doit se terminer. Si le thread effectue une boucle, on peut placer dans cette boucle
un test sur la variable globale. Le thread principal place cette variable une valeur principale au
moment de quitter, ce qui provoque l'arrt des autres threads. Notons tout de mme que le thread
principale devra attendre que l'ensemble des threads ait termin avant de stopper l'application. En
effet, l'arrt de l'application provoque une fermeture de tous les threads en cours (ce qui revient un
appel de TerminateThread() ). Il faudra donc placer un systme d'attente dans le thread principal
(fonctionnant via d'autres variables globales ou par un systme de messages de notification).
Page 57 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
L'utilisation de variables globales est donc une mthode rapide de communication. Toutefois
cette mthode est soumise un certain nombre de contraintes qui ne doivent surtout pas tre
ignores.
Les messages constituent la deuxime mthode de communication. La fonction
PostThreadMessage() permet d'envoyer des messages un thread, mme si celui-ci ne gre pas de
fentres. Le thread pourra alors utiliser les fonctions GetMessage() ou PeekMessage() pour
rcuprer les messages. Bien entendu, il sera inutile d'utiliser les fonctions TranslateMessage() et
DispatchMessage() puisque ces messages ne sont pas destins des fentres.
Il existe plusieurs mthodes pour crer des messages personnaliss. Tout d'abord, il est
possible de dfinir une constante au niveau du prprocesseur (#define). Les valeurs valides pour les
messages dfinis par l'utilisateur doivent tre compris entre les valeurs WM_USER et 0x7FFF. La
constante WM_USER est dfinie dans les headers Windows.
La deuxime mthode consiste utiliser la fonction RegisterWindowMessage(). Cette
fonction retourne une valeur utilisable pour un message en fonction d'une chane de caractre. Le
systme garantit l'unicit du message en fonction de la chane de caractre. Si deux applications
distinctes utilisent cette fonction avec la mme chane, elles obtiendront la mme valeur. Cette
fonction doit donc seulement tre utilise dans le cadre de la communication inter-applications, c'est
dire au niveau systme. Si les messages sont locaux l'application, il est recommand de dfinir
des constantes.
Si un thread dsire communiquer une plus grande quantit d'information, il peut joindre au
message 2 valeurs (lParam et wParam). Deux choix sont alors possibles. Utiliser ces valeurs comme
paramtres ou joindre un pointeur. La mthode utiliser pour joindre un pointeur est la suivante : le
thread qui poste le message alloue dynamiquement de la mmoire et poste le pointeur. Le thread
metteur ne doit pas dsallouer cette mmoire. C'est le thread rcepteur qui s'occupera de la
dsallocation de la mmoire. Cette mthode assure que le pointeur sera toujours valide la rception
du message.
9. Sections Critiques
Cours thorique :
Les sections critiques sont utilises dans le cadre de la synchronisation, de manire raliser
des exclusions mutuelles. Elles permettent de crer une section de code qui sera protg, c'est dire
qu'un seul thread la fois aura accs ce code. Les sections critiques sont trs utiles pour protger
des donnes partages.
L'utilisation des sections critiques est relativement simple. Pour crer une section critique,
l'application doit dclarer une variable de type CRITICAL_SECTION, gnralement globale. Puis
au moins un thread doit initialiser la section critique en appelant la fonction
InitializeCriticalSection(). Cette fonction peut tre appele un nombre indfini de fois. Par
Page 58 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
exemple, chaque thread qui va utiliser la section critique peut s'assurer qu'elle est valide en
appelant cette fonction.
Deux fonctions seront ensuite utilises pour signifier l'entre et la sortie d'un bloc protg. La
fonction EnterCriticalSection() permet de signaler l'entre dans un bloc protg. Si un thread a dj
appel cette fonction et n'est pas encore sorti du bloc protg, cette fonction mettra en attente
n'importe quel autre thread. Le mme thread peut appeler cette fonction plusieurs fois sans tre
bloqu, mais il devra alors appeler la fonction de sortie de bloc un nombre de fois gal pour indiquer
sa sortie du bloc protg.
Un thread indique qu'il sort d'un bloc protg en appelant la fonction LeaveCriticalSection().
Un des threads en attente sera alors dbloqu et pourra son tour excuter le bloc protg.
10. Fonctions d'attente
Cours thorique :
Les fonctions d'attente permettent d'attendre de manire explicite qu'un vnement se produise.
Les fonctions d'attente sont WaitForSingleObject() et WaitForMultipleObjects(). Ces fonctions
permettent d'attendre que l'tat d'un objet change. Le thread appelant une de ces fonctions peut
dfinir un temps maximal d'attente (qui sera ventuellement infini).
Diffrents objets peuvent tre utiliss. Par exemple, la fonction WaitForSingleObject() peut
tre utilise avec le HANDLE d'un thread. La fonction attendra alors que le thread se termine pour
retourner. Pour attendre que plusieurs threads se terminent, une application pourra alors appeler la
fonction WaitForMultipleObjects() en spcifiant qu'elle dsire attendre que l'ensemble des threads
ait termin leur excution. Ces fonctions mettent le thread qui les appelle en attente et sont donc
excellentes au niveau performance. Leur utilisation est donc hautement recommande.
Un vnement cr explicitement peut galement tre utilis avec ces fonctions. Ce cas sera
tudi dans la partie consacre aux vnements. De mme, le HANDLE d'un processus peut tre
utilis de manire attendre qu'il se termine.
Dans le cas de la communication inter-threads nous avons vu qu'une application pouvait
utiliser une variable globale pour spcifier ses threads qu'ils devaient se terminer. L'application doit
ensuite attendre que les threads se terminent avant de quitter. Cette attente sera typiquement ralise
par un appel la fonction WaitForMultipleObjects(). Lorsque cette fonction retournera,
l'application sera alors assure que l'ensemble des ses threads auront termins et pourra quitter sans
risque.
11. Evnements
Cours thorique :
Page 59 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Les vnements peuvent tre utiliss dans les fonctions d'attente. L'API Windows fournit des
fonctions permettant de crer des vnements de manire explicite. L'tat de ces vnements pourra
ensuite tre modifi de manire explicite. L'tat d'un vnement peut tre signal ou non signal.
Lorsqu'un vnement est en tat non signal, l'appel une fonction d'attente provoquera la
suspension du thread appelant jusqu' ce que l'vnement passe en tat signal.
Les fonctions CreateEvent() et CloseHandle() peuvent tre utilises pour la cration et la
destruction d'vnements. Les fonctions SetEvent(), ResetEvent() et PulseEvent() pourront ensuite
tre utilises pour modifier l'tat de l'objet.
Les vnements peuvent tre configurs de diffrentes manires. La configuration d'un
vnement se fait lors de sa cration. Si l'objet est en mode 'rinitialisation manuelle' il restera en tat
signal tant que son tat ne sera pas explicitement rinitialis grce la fonction ResetEvent(). Si
plusieurs threads taient en attente de cet objet, ils seront alors tous librs au moment ou l'tat de
l'objet passera 'signal'.
Si l'objet est en mode 'rinitialisation automatique', son tat sera automatiquement replac
'non signal' ds qu'un thread en attente sera libr. Si plusieurs threads sont en attente d'un mme
vnement, alors un seul thread sera libr eu moment ou l'tat sera pass 'signal'.
Les vnements sont un moyen pratique de synchroniser plusieurs threads de manire
explicite.
Les vnements peuvent par exemple tre utiliss pour mettre un thread en attente tandis qu'un
autre thread initialise des donnes. Ds que les donnes seront initialises, le ou les threads seront
dbloqus par le thread ayant initialis les donnes.
Tout comme les smaphores, les vnements peuvent tre nomms ou non. Si l'vnement est
nomm, son nom doit tre unique dans l'ensemble du systme. Si une autre application tente de crer
un vnement du mme nom, la fonction CreateEvent() retournera un HANDLE sur l'vnement
prcdemment cr. Ceci peut tre utilis pour synchroniser des applications entre elles.
12. Smaphores
Cours thorique :
Les smaphores sont utiliss pour limiter le nombre de threads en fonctionnement. Prenons le
cas d'un serveur. On dsire limiter le nombre maximal de connections simultanes. Dans ce cas, on
utilisera la synchronisation fournie par les smaphores.
Pour utiliser un smaphore, une application doit crer une objet de type smaphore en appelant
la fonction CreateSemaphore(). Un smaphore est constitu d'un compteur. Le statut d'un
smaphore est signal lorsque le compteur est suprieur 0 et non signal lorsque le compteur est
0. Lors de la cration du smaphore, on spcifie la valeur initiale du compteur, ainsi que sa valeur
maximale. La valeur maximale du compteur correspond au nombre maximum de threads qui
pourront tre excuts simultannment.
Page 60 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Chaque thread dsirant se synchroniser doit utiliser une fonction d'attente
(WaitForSingleObject() par exemple). Lorsqu'un thread appelant une fonction d'attente est libr, le
compteur du smaphore est dcrment de 1. Ds que la valeur du compteur atteint 0, les threads
appelant les fonctions d'attente seront bloqus. Lorsqu'un thread sort d'un bloc synchronis, il doit
appeler la fonction ReleaseSemaphore(). Cette fonction incrmente de 1 le compteur du smaphore,
permettant ainsi un autre thread d'tre dbloqu.
Le compteur du smaphore ne pourra jamais dpasser la valeur maximale spcifie au dbut.
De cette manire, on peut trs facilement limiter le nombre de threads effectuant une tche spcifie,
et conserver ainsi les performances systme. Une augmentation trop importante du nombre de
threads en excution simultane poserait en effet des problmes de performances.
Il est possible de spcifier un nom lors de la cration du smaphore. De cette manire, des
applications diffrentes peuvent se synchroniser. La premire cre un smaphore nomm. La
seconde application tente ensuite de crer un smaphore du mme nom. Ceci aura pour effet de lui
retourner un HANDLE sur le smaphore dj existant. Le nom d'un smaphore doit donc tre unique
dans l'ensemble du systme. Remarquons qu'un smaphore ne doit pas ncessairement tre nomm.
13. Trois projets simples
Cours thorique :
Voici 3 projets prsentant l'utilisation des fonctions de synchronisation tudies : sections
critiques, vnements et smaphores. Ces 3 projets ont pour but d'expliquer l'utilit des diffrents
types de synchronisation. De plus, ils pourront aider comprendre la mise en place d'une
synchronisation rudimentaire.
Il ne faut jamais oublier que les accs la console Windows doivent tre synchroniss de
manire viter des rsultats totalement imprvisibles. Une section critique est parfaitement adapte
pour rsoudre ce genre de problmes.
Tlcharger les projets comments : Projet 09 - Projet 10 - Projet 11.
14. Performances
Cours thorique :
La cration d'applications multithread est gnralement trs performante. Toutefois, il faudra
faire attention prserver les performances systmes. La synchronisation devra toujours tre
effectues grce aux objets fournis par l'API comme les sections critiques, les vnements ou encore
les smaphores. Il faut toujours penser qu'un thread inactif doit laisser le contrle au systme et non
pas effectuer une boucle. Bien qu'elles fonctionnent parfaitement, les boucles sont dsastreuses au
niveau performances systme et on perd rapidement l'intrt de l'application multithread.
Page 61 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php
Il ne faut pas non plus tenter de segmenter tout prix les tches. Un nombre trop important de
threads peut nuire aux performances systme. Le travail qui peut tre effectu par un thread ne doit
donc pas tre confi 2 threads.
Les applications clients rseau sont souvent trs performantes grce au multithreading. En
effet, le fait d'tablir plusieurs connections avec la machine hte permet d'amliorer le dbit rseau.
Toutefois, un nombre trop important de threads finira par nuire aux performances de l'application car
le systme perd du temps en passant le contrle du processeur un nombre trop important de
threads. Les applications sont alors considrablement ralenties.
De plus, la ralisation d'applications multithread est souvent source d'erreurs. En effet, il ne
faut jamais oublier que des instructions situes dans des threads seront excutes dans un ordre
quelconque. Il ne faut jamais tenter de prvoir l'ordre d'excution. A partir du moment o l'ordre
d'excution n'est pas certain, il faudra imprativement utiliser les fonctions de synchronisation.
Il faut galement comprendre que le fait d'utiliser des fonctions de synchronisation nuit aux
performances de l'application. Les fonctions de synchronisation ne doivent donc pas tre utilises si
elles ne sont pas ncessaires. De plus, si un nombre important de threads partage des donnes, le
temps d'attente pour obtenir l'accs ces donnes va rapidement devenir important. Chaque thread
passera donc un temps important attendre, ce qui est d'autant plus problmatique si la tche qu'il
doit effectuer est courte.
Il faut galement viter imprativement des entres - sorties successives dans des sections
critiques. En effet, si le temps d'excution entre les sections critiques est trop court, le thread aura
trs rapidement un temps d'attente suprieur son temps de calcul. Ce problme se manifestera
encore plus avec un nombre important de threads. Si un thread doit faire des accs successifs des
donnes partages, il peut copier ces donnes dans un espace non partag ou rester dans la section
critique le temps d'excuter sa tche. Si le temps d'excution de la tche est court, un thread aura tout
intrt ne pas sortir de la section critique.
Il faut donc imprativement prendre en compte les questions de performance lors de la
ralisation d'applications multithread. De plus, les plantages dus des problmes de partage de
donnes ne surviendront pas forcment lors des tests car les threads ne s'excutent jamais de la mme
manire. Il faut donc prvoir les problmes de partage et non pas compter sur les tests pour les
dceler.
Page 62 de 62
23/11/03 http://bob/php/tutapiwin/cur/full.php

Vous aimerez peut-être aussi