Handle sur un contexte de périphérique
Petit rappel sur les fenêtres
Elle peut être composée de différents éléments:
- Barre de titre
- Ascenseurs
- Menus
Et surtout d'une zone client.
Retour au GDI
Vous ne pouvez pas accéder directement à une zone client. Vous ne récupèrez pas lors de la création d'une fenêtre une adresse sur cette zone client. Et la raison à cela est simple: Vous ne connaissez rien du périphérique qui va afficher cette zone client (cela peut-être n'importe quoi: un écran, une imprimante). Si vous deviez adresser directement un périphérique, alors vous vous retrouveriez comme sous DOS, c'est à dire: faire des routines spécifiques pour chaques périphériques !!!
Pour dessiner dans la zone client, vous utiliserez le GDI (Graphic
Device Interface), qui va se débrouiller avec les drivers installés
pour effectuer l'opération graphique demander sur le périphérique
visé. Pour effectuer une opération graphique, il vous faut tout
d'abord demander un handle sur un périphérique appelé "handle
sur un contexte de périphérique" (device context de type
HDC) et c'est grâce à ce handle que vous pourrez ensuite dessiner,
via le GDI qui va l'utiliser pour connaître le périphérique
physique impacté par cette opération. Pour un écran, ce
handle est généralement associé à une fenêtre.
Comment récupérer un hdc ?
Dans la procédure associée à la fenêtre (celle qui gére les messages), il existe au moins deux possibilités:
Soit lors du traitement d'un message WM_PAINT:
Dans ce cas, c'est la fonction BeginPaint qui va retourner un hdc:
HDC BeginPaint(HWND hwnd, LPPAINTSTRUCT lpPaint);
Si la valeur de sortie est NULL, alors il n'y a pas de device contexte disponible.
En sortie, lpPaint pointeur sur structure PAINTSTRUCT qui contient les informations suivantes:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
Avec hdc qui est le handle sur le device context (encore !)
fErase qui indique que Windows a effacé ou non la zone invalide. fErase = true en général et indique que Windows a effacé la zone invalide en utilisant la brosse spécifié dans hbrBackground. le Seul cas ou fErase = false, c'est avec l'utilisation de la fonction InvalidateRect(expliquée plus bas) où un paramètre indique que l'on veut ou non un effacement de la zone.
rcPaint qui est une structure de type RECT et qui va contenir les coordonnées de la zone invalide
Le reste étant réservé...
Remarque: rgbReserved était à 16 en version 16 bits.
Lorsque l'on
récupère un contexte de périphérique sur une fenêtre,
Windows va définir une zone de clipping
qui va correspondre à la zone invalide.
Si lors d'un événement WM_PAINT, vous trouvez que la zone de clipping est insuffisante, vous pouvez utiliser la commande InvalidateRect avec les paramètres suivant:
InvalidateRect (hwnd, NULL, TRUE); on invalide alors la totalité de la zone (Remarque sur TRUE !!! C'est lui qui va positionner fErase cité précédemment)
BOOL InvalidateRect(HWND hWnd,CONST RECT* lpRect,BOOL bErase);
Si Hwnd est NULL, alors se seront toutes les fenêtres qui recevront l'invalidation.
lpRect pointeur sur structure de type RECT qui contiendra les coordonnées de la zone invalide. NULL pour indiquer que toute la zone client est invalide.
enfin bErase qui va demander ou non a Windows d'effacer la zone invalide. Cette valeur est récupérée par BeginPaint de la fenêtre impactée dans la structure PAINTSTRUCT.
Soit lors du traitement d'un message quelconque
Dans ce cas, utilisation de la fonction GetDC. La zone de clipping correspond dans ce cas à toute la zone client.
Remarque sur GetDC:
HDC GetDC(HWND hWnd);
Si hWnd est NULL, alors c'est l'écran complet qui sera capturé.
Libérer le contexte !
Lorsque l'on récupère un contexte de périphérique dans la procédure de la fenêtre, il faut impérativement le libérer avant de sortir de cette procédure. Il ne sera de toute façon plus utilisable lors d'un prochain appel à la procédure de la fenêtre. Par exemple, si Windows retourne un nouveau WM_PAINT, c'est qu'une nouvelle zone de la fenêtre est invalidée, et ce ne sera pas forcément la même zone invalidée que précédemment !
D'où les deux fonctions:
- EndPaint suite à l'utilisation de BeginPaint: EndPaint valide la zone, et va libérer aussi le contexte de périphérique.
BOOL EndPaint(HWND hWnd,CONST PAINTSTRUCT *lpPaint);
Avec hWnd qui est toujours le handle notre fenêtre
et lpPaint qui est le pointeur sur une structure PAINTSTRUCT récupéré
de BeginPaint.
Remarque:Si BeginPaint et EndPaint ne sont pas dans le traitement du message, la fenêtre ne pourra jamais être valide. Windows renverra alors de nouveau un WM_PAINT...
- ReleaseDC dans le cas d'une utilisation de la fonction GetDC (Remarque, cette fonction ne valide pas la zone client !): ReleaseDC (HWND hwnd, HDC hdc);
Autres solutions pour récupérer des handles sur des contextes de périphériques
Il existe aussi deux autres fonctions pour récupérer un handle de contexte:
HDC GetWindowDC(HWND hWnd) pour accéder à l'intégralité de votre fenêtre, y compris les barres de titres. On ne se limitera donc pas à la zone client.
ou encore CreateDC un peu plus compliqué...donc non étudié maintenant !
Il existe aussi une fonction un peu particulière qui va permettre de créer un contexte de périphérique en mémoire qui sera compatible avec un contexte de périphérique existant. Cette fonction est CreateCompatibleDC() et va donc retourner un handle sur le contexte de périphérique en mémoire ainsi créé. Cette fonction étant un peu plus complexe, il vous faudra attendre de voir d'autres notions si vous êtes débutant. Pour les plus préssés voyez ce chapitre.
Remarques concernant la zone client
Nous venons de voir qu'il était possible de récupérer les coordonnées du rectangle invalide (sous la forme RECT) lors d'un événement WM_PAINT. Mais il peut être intéressant de connaître la taille de la zone client. En effet, cette zone client peut varier suivant les modifications effectuées par l'utilisateur sur la taille de la fenêtre. Pour connaître cette taille, il faudra utiliser la fonction GetClientRect(). Il faut au préalable avoir un handle sur une zone client, même si l'on ne passe pas ce handle à la fenêtre.
BOOL GetClientRect(
HWND hWnd,
LPRECT lpRect
);
Avec lpRect qui est un pointeur sur une structure
RECT qui recevra les informations de la zone client.
De la même manière, pour récupérer la taille de
la fenêtre, on utilisera la fonction GetWindowRect (HWND hWnd, LPRECT
lpRect);
Il ne sera pas nécessaire pour cette fonction de faire un GetWindowDC()
avant...