Mémo WIN 32 Michel Llibre - 2007 ------------------------------------- POINT D'ENTREE DU PROGRAMME PRINCIPAL ------------------------------------- int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, PSTR szCmdLine,int iCmdShow) hInstance : Numéro d'instance de cette application hPrevInstance : Numéro de la dernière instance encore active de l'application (souvent ignoré) szCmdLine : String de la ligne de commande (souvent ignoré) iCmdShow : Manière d'afficher la fenêtre (généralement ignoré) Programme très simple sans fenêtre (Hello world !) : -------------------------------------------------- #include int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox( NULL, "Hello World !", "Message :", 0 ); return 0; } Programme très simple avec fenêtre : ------------------------------------- #include LRESULT CALLBACK WndProc01 (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "ClassBidon" ; //Nom de la classe de la fenêtre créée HWND hWnd ; MSG msg ; WNDCLASSEX wndclass ; // 1 - Définir la classe de la fenêtre (ordinaire) wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; // à redessiner si modif taille wndclass.lpfnWndProc = WndProc01 ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; // 2 - Enregistrer la définition RegisterClassEx (&wndclass) ; // 3 - Créer une fenêtre de cette classe (reconnue par son nom) en spécifiant son style hWnd = CreateWindow (szAppName, // nom de la classe de la fenêtre créée. "Vasistas !!!", // titre de la fenetre WS_OVERLAPPEDWINDOW, // style de la fenetre CW_USEDEFAULT, // position initiale en x CW_USEDEFAULT, // position initiale en y CW_USEDEFAULT, // largeur initiale CW_USEDEFAULT, // hauteur initiale NULL, // handle de la fenetre mere NULL, // handle du menu de la fenetre hInstance, // handle de l'instance NULL) ; // parametres de creation // 4 - Indiquer comment la fenêtre doit être affichée pour la première fois (cf iCmdShow) ShowWindow (hWnd, iCmdShow) ; // 5 - Provoquer l'affichage de la fenêtre (envoi DIRECT de WM_PAINT si nécessaire) UpdateWindow (hWnd) ; /* 6 - Distribuer les messages jusqu'à réception du message WM_QUIT (produit par un PostQuitMessage(0) par exemple. */ while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } // 7 - Sortie finale return msg.wParam ; } // Procédure de traitement de tous les messages destinés à sa fenêtre. LRESULT CALLBACK WndProc01 (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; TEXTMETRIC tm ; // Pour la taille des lettres RECT rect ; SIZE siz ; POINT pt1, pt2 ; static char salut[] = "Salut à tous !" ; static int cxChar, cyChar ; switch (iMsg) { case WM_CREATE : // Effectuer ici toutes les initialisations avant tracé dans la zone // client hdc = GetDC(hWnd) ; GetTextMetrics(hdc, &tm) ; // Caractéristiques police cxChar = tm.tmAveCharWidth ; // Largeur moyenne d'un caractère cyChar = tm.tmHeight; // Hauteur totale caractère (jambe et accent compris) ReleaseDC(hWnd,hdc) ; return 0 ; case WM_PAINT : // Redessiner ici le contenu de la fenêtre hdc = BeginPaint (hWnd, &ps) ; GetClientRect (hWnd, &rect) ; // Dimension zone client SetTextAlign(hdc, TA_CENTER) ; // Centrage du texte en x // Ecriture du texte au centre de la zone TextOut(hdc, rect.right/2, rect.bottom/2, salut, strlen(salut)) ; // Dimesions du texte écrit GetTextExtentPoint32(hdc,salut,strlen(salut), &siz) ; // On calcule le rectangle circonscrit au texte pt1.x = rect.right/2 - (siz.cx + cxChar)/2 ; pt1.y = rect.bottom/2 ; pt2.x = pt1.x + siz.cx + cxChar ; pt2.y = pt1.y + siz.cy ; // On passe en mode de remplissage transparent (pour ne pas effacer le texte) SelectObject(hdc,GetStockObject(NULL_BRUSH)) ; // On entoure le texte d'un rectangle Rectangle(hdc,pt1.x,pt1.y,pt2.x,pt2.y) ; EndPaint (hWnd, &ps) ; return 0 ; case WM_DESTROY : // Il faut détruire la fenêtre PostQuitMessage (0) ; // On envoie le WM_QUIT return 0 ; } // Traitemement par défaut des messages non traités précédemment return DefWindowProc (hWnd, iMsg, wParam, lParam) ; } La boucle de répartition des messages : ------------------------------------- Sous sa forme la plus simple elle s'écrit : while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } Sous cette forme, lorsqu'il n'y a pas de message pour l'application, GetMessage bloque le programme. Lorsque GetMessage reçoit WM_QUIT, il renvoit 0, ce qui entraîne la sortie du while. S'il reçoit un autre message, il renvoie TRUE. Le message msg est alors traité par TranslateMessage et DispatchMessage active la procédure fenêtre adéquate. Si on désire faire travailler l'application en l'absence de message, on peut remplacer GetMessage par PeekMessage qui rend le contrôle à l'application quand il y a un message pour elle (comme GetMessage) avec TRUE comme valeur de retour, mais également quand il n' y a aucun message dans aucune des files des applis fonctionnant sous Windows, avec FALSE comme valeur de retour. Comme quand PeekMessage reçoit WM_QUIT, il retourne TRUE, il faut tester cette valeur pour terminer l'appli. Exemple : while (TRUE) { if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { if(msg.message == WM_QUIT) break ; TranslateMessage (&msg) ; DispatchMessage (&msg) ; } else { // autre travail du programme } } Exemple : RandRect chap4, page 181 du Petzold. La procédure par défaut : (LRESULT) l = DefWindowProc (hWnd, iMsg, wParam, lParam) ; renvoi un résultat qui dépend de l'évènement traité. Cette procédure peut être utilsé ailleurs et le résultat utilisé à toutes fins utiles. Lorsque l'usager demande la fin d'une appli, il y a envoi du message WM_CLOSE. L'appli peut le traiter pour retarder ou annuler la demande. Le traitement par défaut effectué par DefWindowProc(..) produit l'envoi d'un message WM_DESTROY. On exploite en général ce message pour faire les dernières traitements (désallocations par exemple) et envoyer le WM_QUIT (par PostQuitMessage (0)). STRUCTURES DE BASE POUR LES TRACES : ---------------------------------- typedef struct tagPOINT { // pt LONG x; LONG y; } POINT; typedef struct tagSIZE { // siz LONG cx; LONG cy; } SIZE; typedef struct _RECT { // rect LONG left; LONG top; LONG right; LONG bottom; } RECT; Obtenir un handle de DC pour pouvoir écrire et dessiner dans une fenêtre : ------------------------------------------------------------------------ Les tracés dans une fenêtre "hWnd" passent systématiquement par l'intermédiaire de tracés dans un "contexte de péréphérique" (DC ou device context) afin d'être compatible avec le paramétrage en cours de la carte graphique. On obtient un handle de DC ou hdc (de type "HDC") de deux manières différentes : 1) dans le traitement de WM_PAINT : hdc = BeginPaint(hWnd,&ps) ; .... Ordres de tracé dans le hdc. EndPaint(hWnd, &ps) ; // Instruction obligatoire de pair avec "BeginPaint(hWnd,&ps)" "ps" est une structure locale préalablement déclarée par : PAINTSTRUCT ps ; typedef struct tagPAINTSTRUCT { // ps HDC hdc; // Handle sur le DC a repeindre BOOL fErase; // est à TRUE si le fond de la partie invalide est également à repeindre. // C'est a l'appli de le faire si pas de brosse de fond spécifiée à la création RECT rcPaint; // définit la partie invalide. On ne peut tracer que dans ce rectangle. Ce //qui est tracé à l'extérieur est clippé. BOOL fRestore; // réservé win32 BOOL fIncUpdate; // réservé win32 BYTE rgbReserved[32]; // réservé win32 } PAINTSTRUCT; On peut utiliser directement : ps.hdc WM_PAINT n'est produit par Windows que si un rectangle invalide existe. Apès EndPaint, toute la zone est validée (raz du rectangle invalide). 2) Partout : hdc = GetDC(hWnd) ; .... Ordres de tracé dans le hdc. ReleaseDC(hWnd, hdc) ; // Instruction obligatoire de pair avec "hdc = GetDC(hWnd)" Remarque : hdc = GetDC(NULL) renvoi un handle de DC qui permet de travailler dans tout l'écran. (non documenté par Microsoft). 3) Dans toute la fenêtre : hdc = GetWindowDC(hWnd); On peut tracer sur la barre de titre, menus, barres de défilement, bordures et zone client) par . Il est déconseillé de faire cela. Différences entre (1) et (2,3,..) : Dans (1), on ne peut tracer que dans le rectangle ps.rcPaint (clipping). Apès EndPaint, toute la zone est validée (raz du rectangle invalide). Dans (2,3..), il n'y a pas de clipping. On peut tracer dans toute la zone client. Après ReleaseDC le rectangle invalide n'est pas modifié. Ce qui est tracé dans (2,3..) et pas repété dans (1) va disparaitre dès qu'on manipulera la fenêtre. CONTEXTE MEMOIRE : On peut travailler en mémoire avec un "contexte mémoire", associé à un handle de dc (hdc) préalablement obtenu. On travaille en mémoire par l'intermédiaire d'un handle sur le dc mémoire obtenu par : hdcMem = CreateCompatibleDC(hdc) ; // Si hdc = NULL, il est compatible avec l'écran de l'appli en cours ... dessin en memoire ... DeleteDC(hdcMem) ; ou bien directement par (pour travailler sur un contexte memoire associé à tout l'écran) : hdc = CreateDC("DISPLAY",NULL,NULL,NULL) ; ... tracés et/ou accés aux info du DC ... DeleteDC(hdc) ; Si on désire seulement des infos, il suffit de créer un "contexte d'information" IC : hdcInfo = CreateIC("DISPLAY"NULL,NULL,NULL) ; ... accés aux infos ... DeleteDC(hdcInfo); Informations sur le périphérique et sur le contexte de périphérique : ------------------------------------------------------------------- GetDeviceCaps permet d'obtenir des informations sur le périphérique d'affichage hard associé au contexte hdc : int GetDeviceCaps(HDC hdc,int nIndex); Exemples d'information demandée suivant la valeur de nIndex : HORZSIZE, "Largeur en millimètres :", VERTSIZE, "Hauteur en millimètres :", HORZRES, "Largeur en pixels :", VERTRES, "Hauteur en pixels :", BITSPIXEL, "Nombre de Bits par couleur :", PLANES, "Nombre de plans couleur :", NUMBRUSHES, "Nombre de brosses :", NUMPENS, "Nombre de stylos :", NUMMARKERS, "Nombre de marqueurs :", NUMFONTS, "Nombre de polices :", NUMCOLORS, "Nombre de couleurs :", PDEVICESIZE, "Taille de la structure :", ASPECTX, "Largeur relative d’un pixel :", ASPECTY, "Hauteur relative d’un pixel :", ASPECTXY, "Diagonale relative d’un pixel :", LOGPIXELSX, "Points horizontaux par pouce :", LOGPIXELSY, "Points verticaux par pouce :", SIZEPALETTE, "Taille de la palette :", NUMRESERVED, "Entrées réservées :", COLORRES, "Résolution couleur réelle :" ASPECTX, ASPECTY et ASPECTXY renvoi des valeurs relatives les unes aux autres. Dans le cas d'un écran,HORZSIZE, VERTSIZE, LOGPIXELSX et LOGPIXELSY correspondent à un écran fictif généralement plus petit que l'écran réel. Une appli peut changer dynamiquement la résolution de l'écran par ChangeDisplaySettings (pour les applis plein écran). GetSystemMetrics permet d'obtenir des informations sur le système d'affichage : dimensions des divers éléments des fenêtres, paramètres de la souris, .... Cette fonction ne fait pas partie du GDI (interface de périphériques graphiques : Graphics Device Interface). Elle n'a pas besoin de hdc en paramètre d'appel. Les dimensions retournées sont en pixels. int GetSystemMetrics(int nIndex); Exemples d'information demandée suivant la valeur de nIndex : SM_CXSCREEN, "Largeur écran en pixels", SM_CYSCREEN, "Hauteur écran en pixels", SM_CXVSCROLL, "Largeur flèche défilement V.", SM_CYHSCROLL, "Hauteur flèche defilement H.", SM_CYCAPTION, "Hauteur barre de titre", SM_CXBORDER, "Largeur bordure fenêtre", SM_CYBORDER, "Hauteur bordure fenêtre", SM_CXDLGFRAME, "Largeur cadre fenêtre dialogue", SM_CYDLGFRAME, "Hauteur cadre fenêtre dialogue", SM_CYVTHUMB, "Hauteur curseur défilement V.", SM_CXHTHUMB, "Largeur curseur défilement H.", SM_CXICON, "Largeur icône", SM_CYICON, "Hauteur icône", SM_CXCURSOR, "Largeur curseur", SM_CYCURSOR, "Hauteur curseur", SM_CYMENU, "Hauteur barre de menu", SM_CXFULLSCREEN, "Largeur maximum zone client plein écran", SM_CYFULLSCREEN, "Hauteur maximum zone client plein écran", SM_CYKANJIWINDOW, "Hauteur fenêtre Kanji", SM_MOUSEPRESENT, "Indicateur de présence souris", SM_CYVSCROLL, "Hauteur flèche défilement V.", SM_CXHSCROLL, "Largeur flèche défilement H.", SM_DEBUG, "Indicateur version de debogage", SM_SWAPBUTTON, "Indicateur d'inversion boutons", SM_CXMIN, "Largeur minimale fenêtre", SM_CYMIN, "Hauteur minimale fenêtre", SM_CXSIZE, "Largeur icône Reduire/Agrandir", SM_CYSIZE, "Hauteur icône Reduire/Agrandir", SM_CXFRAME, "Largeur cadre fenêtre", SM_CYFRAME, "Hauteur cadre fenêtre", SM_CXMINTRACK, "Déplacement min. fenêtre en x", SM_CYMINTRACK, "Déplacement min. fenêtre en y", SM_CXDOUBLECLK, "Tolérance double clic en x", SM_CYDOUBLECLK, "Tolérance double clic en y", SM_CXICONSPACING, "Espace horizontal entre icônes", SM_CYICONSPACING, "Espace vertical entre icônes", SM_MENUDROPALIGNMENT,"Déroulement menu bouton G ou D", SM_PENWINDOWS, "Extensions stylo installées", SM_DBCSENABLED, "Caractères 16 bits autorisés", SM_CMOUSEBUTTONS, "Nombre de boutons souris", SM_SHOWSOUNDS, "Présenter visuellement les sons" Remarque : Dans le cas d'un écran, GetDeviceCaps(hdc,HORZRES) est identique à GetSystemMetrics(SM_CXSCREEN) et GetDeviceCaps(hdc,VERTRES) est identique à GetSystemMetrics(SM_CYSCREEN). Les attributs du contexte de péréphérique : ----------------------------------------- Les caractéristiques de l'apparence de tout ce qui est écrit (de la police, du stylo, de la brosse, du mode de fond, de la couleur de fond, de la couleur du texte, de l'espacement) ou tracé (bitmap, palette, region, mode de tracé, mapping mode) sont des attributs du DC : Attribut Valeur défaut Modifier Lire ----------------------------------------------------------------------------- Mode Topographique MM_TEXT SetMapMode GetMapMode Origine fenêtre (0,0) SetWindowOrgEx GetWindowOrgEx OffsetWindowOrgEx Origine viewport (0,0) SetViewportOrgEx GetViewportOrgEx OffsetViewportOrgEx Extensions fenêtre (1,1) SetWindowExtEx GetWindowExtEx SetMapMode ScaleWindowExtEx Extensions viewport (1,1) SetViewportExtEx GetViewportExtEx SetMapMode ScaleViewportExtEx Stylo (Pen) BLACK_PEN SelectObject SelectObject Position courante stylo (0,0) MovetoEx, LineTo GetCurrentPositionEx PolylinTo, PolyBezierTo Mode de tracé R2_COPYPEN SetROP2 GetROP2 Brosse (Brush) WHITE_BRUSH SelectObject SelectObject Origine brosse (0,0) SetBrushOrgEx GetBrushOrgEx Mode remplissage ALTERNATE SetPolyFillMode GetPolyFillMode Mode de dilatation BLACKONWHITE SetStetchBltMode GetStetchBltMode Police (Font) SYSTEM_FONT SelectObject SelectObject Bitmap Aucun SelectObject SelectObject Mode Fond OPAQUE GetBkMode SetBkMode Couleur Fond Blanche GetBkColor SetBkColor Couleur Texte Noire GetTextColor SetTextColor Espacement 0 GetTextCharacterExtra SetTextCharacterExtra Zone de clipping Aucune SelectObject GetClipBox SelectClipRgn IntersectClipRgn OffsetClipRgn ExcludeClipRgn SelectClipPath Sauvegarde des attributs du DC : ------------------------------- Les attributs du DC reprennent leurs valeurs par défaut à chaque GetDC(hWnd) ou BeginPaint(hWnd, &ps) sauf si lors de l'enregistrement de la classe de la fenêtre on a choisit le style CS_OWNDC : wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ; // DC memorise les modifs Dans ce cas, on a intérêt à effectuer toutes les modifs que l'on désire maintenir permanentes lors du traitement du WM_CREATE. ON A INTERET à utiliser ce style pour les applis qui font beaucoup de graphique, et mémoriser le DC une fois pour toutes au traitement du WM_CREATE. Il est alors inutile au traitement du WM_PAINT d'utiliser la paire BeginPaint, EndPaint, mais Normalement on termine par un : ValidateRect(hWnd,NULL); pour indiquer à Windows que c'est repeint. Si on n'effectue pas cette validation, on peut augmenter la rapidité des rappels. On peut également utiliser le style CS_CLASSDC qui fait de même, mais pour toutes les applis qui utilisent cette classe de fenêtre (peut entraîner des résultats bizarres). On peut également faire une sauvegarde temporaire en utilisant la fonction : iSaveID = SaveDC(hdc) ; et retrouver cet etat sauvegardé par un : RestoreDC(hdc, iSaveDC) ; Les états sauvegardés sont empilés. Lorsqu'un état est restauré, tous ceux qui ont eté empilés après celui-la sont perdus. L'appel de RestoreDC avec un numéro negatif permet de restaurer un état relativemnt à son rang par rapport à l'état courant. Pour restaurer l'état qui existait avant le dernier SaveDC, il suffit de faire : RestoreDC(hdc,-1) ; Les metafiles : --------------- C'est un format graphique de type vectoriel. METAFILE : Créer le Métafile avant tout tracé : HDC hdcMeta = CreateEnhMetaFile(hdcRef,pFilename,pRectMeta,pDescription) ; Faire les tracés dans le metafile ... Fermer le Metafile (Nécessaire avant affichagepour obtenir hemf) : HEHMETAFILE hemf = CloseEnhMetaFile(hdcMeta) ; On l'affiche (ds WM_PAINT en general) : PlayEnhMetafile(dc, hemf, pRectlog) ; On le détruit (ds WM_DESTROY en général) : DeleteEnhMetafile(hemf) ; Définition d'un rectangle metafile cohérent ------------------------------------------- Si pRect (rectangle qui encadre tout le tracé) est NULL, le GDI calcule celui qui encadre la figure (problématique). Les tracés usagers sont effectués dans une certaine unité ux limités à uxmax. Si on les dirige vers un certain "device" dont la résolution est limitée à pixxmax = GetDeviceCaps(hdc,HORZRES) , on converti les ux en pixx au moyen du facteur ux2pixx = pixxmax/uxmax : pixx = ux * ux2pixx. Les dimensions d'un rect-Metafile sont spécifiées en 1/100 de mm. Si on utilise les fonctions de tracé vers le métafile (qui envoient des pixx) la dimension max de la figure générée est alors supposée être de celle du device précédent, à savoir, GetDeviceCaps(hdc,HORZSIZE) en mm. Il faudrait donc, dans ce cas spécifier un rectMeta.right de : rectMeta.right = 100*GetDeviceCaps(hdc,HORZSIZE) ; (Même raisonnement en vertical) Si les tracés sont directement effectués en ux, il faut utiliser : rectMeta.right = 100*GetDeviceCaps(hdc,HORZSIZE)*(uxmax/GetDeviceCaps(hdc,HORZRES)) Affichage ou non du metafile ---------------------------- Si pFileName est NULL c'est un metafile en mémoire destiné a priori a l'affichage ou a l'impression. Sinon, c'est un pointeur sur le nom du fichier créé pour enregistrer les tracés qui peut être chargé par une autre appli. Dans ce cas, on peut faire le DeleteEnhMetafile juste après le CloseEnhMetaFile. Pour afficher le métafile dans un hdc on fera : hemf = GetEnhMetafile(pFilename) ; PlayEnhMetafile(dc, hemf, pRectlog) ; /* Rect en unités de tracé (ux ou pixx) */ DeleteEnhMetafile(hemf) ; Les différents repères physiques : -------------------------------- On distinque trois repères physiques, écran, fenêtre physique, et client ayant chacun leur origine au coin haut gauche du rectangle concerné, et dans lesquels les points sont repérés en pixels. 1 - LE REPERE ECRAN : Ses coordonnées sont utilisées par Windows pour le traitement de WM_MOVE (fenêtre non-enfant), pour le DC écran créé par un CreateDC("DISPLAY",...) et pour les fonctions suivantes : CreateWindow (si non-enfant) MoveWindow (si non-enfant) GetMessagePos, GetCursorPos, SetCursorPos GetWindowRect WindowFromPoint SetBrushOrgEx Elles permettent de travailler dans tout l'écran. 2 - LE REPERE FENETRE PHYSIQUE (entière) Elles interviennent quand on a obtenu le hdc par GetWindowDC. Elles permettent de travailler que dans (et dans toute) la fenêtre hWnd (la barre de titre, etc...). 3 - LE REPERE CLIENT Ses coordonnées interviennent lorsque le hdc a été obtenu par BeginPaint ou GetDC. Elles ne permettent de tracer que dans la zone client. Les 3 fonctions suivantes permettent d'effectuer toutes les conversions : ClientToScreen(hWnd, &pt) ; Convertit pt.x et pt.y des coordonnées client en coordonnées écran. ScreenToClient(hWnd, &pt) ; Convertit pt.x et pt.y des coordonnées écran en coordonnées client. GetWindowRect(hWnd, &rect) ; Renvoie les coordonnées écran de la fenêtre hWnd. Le repère logique : ----------------- A l'exception des coordonnées transmises par Windows par l'intermédiaire de wParam ou lParam pour le traitement des messages WM_SIZE, WM_MOVE, WM_MOUSEMOVE, ..., qui sont toujours en coordonnées physiques (également appelées coordonnées périphériques), des infos fournises par GetSystemMetrics (en pixels), GetDeviceCaps (en mm et pixels), GetWindowRect (en pixels), GetClientRect (en pixels), les autres coordonnées utilisées pour écrire, tracer, obtenir des infos sont pratiquement toutes relatives à un repère logique qui opère avec des unités prédéfinies (pixels, fraction de mm ou de pouce, twips) ou choisies par l'usager. LE REPERE LOGIQUE EST POSITIONNE RELATIVEMENT A UN REPERE PHYSIQUE INTERMEDIAIRE appelé LE VIEWPORT. Le viewport correspond au repère physique sélectionné (écran, fenêtre physique, ou client) à un décalage d'origine près défini par la fonction SetViewportOrgEx : SetViewportOrgEx(hdc, x_Ov, y_Ov, &pt) ; Cette fonction positionne l'origine du Viewport relativement au repère physique de base (en pixels par rapport au coin haut gauche de la zone physique) au point (x_Ov,y_Ov). Le POINT pt reçoit les coordoonnées de l'origine de l'ancien viewport. On peut remplacer &pt par NULL. L'origine du repère logique est positionné relativement à l'origine du viewport. En fait, avec la fonction SetWindowOrgEx on précise la position de l'origine du viewport dans le repère logique. SetWindowOrgEx(hdc, X_Ov, Y_Ov, &pt) ; Cette fonction indique que la position de l'origine du Viewport relativement au repère logique (en unités logiques) est en (X_Ov,Y_Ov). Le POINT pt reçoit les anciennes valeurs de ces coordoonnées. On peut remplacer &pt par NULL. En définitive : 1 - quand on augmente x_Ov avec SetViewportOrgEx, en ne modifiant rien d'autre, les tracés effectués sont décalés vers la droite (x positifs), puisque le repère logique suit la translation imposée au viewport, 2 - quand on augmente XE_L avec SetWindowOrgEx, en ne modifiant rien d'autre, le viewport n'étant pas modifié, c'est le repère logique qui subit la translation en sens opposé. Les tracés effectués seront décalés vers la gauche , c'est-à-dire les x négatifs (en supposant que le facteur de changement d'échelles est positif). Modifications d'unités et de sens des axes : ------------------------------------------ Cette modification est effectuée en sélectionnant un mode topographique à l'aide de la fonction : SetMapMode(hdc, iMapMode) ; Les 8 Modes topographiques prédéfinis sont : Mode Topographique Unité logique x croissent vers y croissent vers -------------------------------------------------------------------------------- MM_TEXT pixel la droite le bas MM_LOMETRIC 0.1 mm la droite le haut MM_HIMETRIC 0.01 mm la droite le haut MM_LOENGLISH 0.01 pouce la droite le haut MM_HIENGLISH 0.001 pouce la droite le haut MM_TWIPS 1/1440 de pouce la droite le haut MM_ISOTROPIC Qcq avec x=y au choix au choix MM_ANISOTROPIC Qcq au choix au choix Le mode par défaut est le mode MM_TEXT (unités logiques et physiques identiques). Pour les modes "métriques", c'est-à-dire MM_LOMETRIC, MM_HIMETRIC, MM_LOENGLISH, MM_HIENGLISH et MM_TWIPS, les sens de l'axe des y est vers le haut, mais, par défaut, les origines viewport et logique sont toujours au coin haut-gauche de la zone. Pour mettre ces 2 origines au coin bas-gauche le plus simple consiste à faire : SetViewportOrgEx(hdc, 0, cyClient, NULL) ; où cyClient est la hauteur en pixels de la zone physique. Pour les modes MM_ISOTROPIC et MM_ANISOTROPIC, et uniquement pour eux, on peut fixer le rapport entre les unités logiques et les pixels à l'aide des 2 fonctions SetViewportExtEx et SetWindowExtEx. SetWindowExtEx(hdc, XE_L, YE_L, &siz) ; SetViewPortExtEx(hdc, xE_p, yE_p, &siz) ; où siz récupère les anciennes valeurs. Remplacer &siz par NULL si inutiles. Si (x_Ov, y_Ov) est la position de l'origine viewport relativement au coin haut-gauche de la zone physique, fixée par SetViewportOrgEx(hdc, x_Ov, y_Ov, &pt), et si (X_Ov,Y_Ov) est la position de l'origine viewport relativement à l'origine logique fixée par SetWindowOrgEx(hdc, X_Ov, Y_Ov, &pt), les coordonnées physiques (x,y) relatives au coin haut-gauche de la zone physique d'un point de coordonnées (X,Y) dans le repère logique, sont obtenues par les formules de changement de coordonnées : x = x_Ov + xE_p*(X - X_Ov)/XE_L ; y = y_Ov + yE_p*(Y - Y_Ov)/YE_L ; et inversement : X = X_Ov + XE_L*(x - x_Ov)/xE_p ; Y = Y_Ov + YE_L*(y - y_Ov)/yE_p ; Les fonctions DPtoLP et LPtoDP font ces conversions. DPtoLP(hdc, &pt, n) ; // Entrée pt={x,y} ; Sortie pt={X,Y} ; LPtoDP(hdc, &pt, n) ; // Entrée pt={X,Y} ; Sortie pt={x,y} ; n est le nombre de points à convertir (pt pointe sur un ou plusieurs points). Les facteurs d'échelles xE_p/XE_L et yE_p/YE_L utilisent 2 valeurs car toutes les opérations sont effectuées sur des entiers 16 bits avec résultats intérmédiares en 32 bits. Toutes les valeurs transmises x_Ov, y_Ov, X_Ov, Y_Ov, xE_p, yE_p, XE_L et YE_L doivent être comprises entre -32768 et 32767. Pour fixer les unités logiques, une manière simple d'utiliser ces "extensions" est de donner aux paramètres xE_p et yE_p les dimensions en pixels de la zone physique et de donner aux paramètres XE_L et YE_L les dimensions attribuées à cette zone en unités logiques. Pour les modes MM_ISOTROPIC et MM_ANISOTROPIC, ces extensions sont fixées (généralement dans cet ordre) par : SetWindowExtEx(hdc, XE_L, YE_L, &siz) ; // si siz != NULL, reçoit les anciennes valeurs SetViewPortExtEx(hdc, xE_p, yE_p, &siz) ; En général, on fixe d'abord avec SetWindowExtEx la taille logique désirée d'une zone et on met dans SetViewportExtEx les dimensions en pixels de cette zone (méthode des correspondances). Pour qu'un axe logique soit de sens opposé à celui d'un axe physique (par exemple pour que l'axe des Y soit ascendant) il faut qu'une seule des deux extensions correspondentes soit négative (yE_p ou YE_L négatif mais pas les deux). En mode MM_ISOTROPIC les facteurs d'échelle en x et y doivent être égaux en valeur absolue |xE_p/XE_L| = |yE_p/YE_L|. S'ils ne le sont pas l'extension du viewport qui correspond au plus grand rapport pixels/unités_logiques est modifié. Elle est diminuée pour que les rapports soient égaux. Dans le cas de la méthode des corresposdances, cela permet de garantir que les deux domaines logiques spécifiés contiendront dans la zone physique. (Cf Petzold pages 167, ...). Comme les extensions viewport sont modifiées en fonction des valeurs des extensions logiques, il faut toujours spécifier les extensions viewport APRES AVOIR spécifié les extensions logiques : 1 - SetWindowExtEx(..) ; 2 - SetViewportExtOrg(..) ; En mode MM_ANISOTROPIC, les facteurs d'échelles sont libres. Lorsqu'on choisit ce mode, on hérite des extensions et origine du mode précédemment utilisé. Il est donc particulièrement utile pour effectuer une légère modification d'un des modes prédéfinis. Par exemple pour inverser le sens de l'axe des y en mode MM_LOMETRIC (pour le mettre croissant vers le bas, comme en MM_TEXT) on fera : SetMapMode(hdc, MM_LOMETRIC) ; SetMatMode(hdc, MM_ANISOMETRIC) ; GetViewportExtEx(hdc, &siz) ; SetViewportExtEx(hdc, siz.cx, -siz.cy, NULL); Pour accéder à la zone client avec une adresse de ligne et colonne caractère (avec une fonte à espacement fixe) on fera : SetMatMode(hdc, MM_ANISOTROPIC); SetWindowExtEx(hdc, 1, 1, NULL); SetViewportExtEx(hdc, cxCHar, cyChar, NULL); Prendre en compte la taille de la zone client : --------------------------------------------- Soit cxClient et cyClient la largeur et la hauteur de la zone client en pixels. La manière la plus simple de mettre à jour ces valeurs est de les mémoriser à chaque changement qui produit l'envoi du message WM_SIZE avec ces deux valeurs codées dans lParam : case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; WM_SIZE est envoyé avant WM_PAINT, mais après WM_CREATE. Si on a besoin de ces dimensions lors du traitement de WM_CREATE il faut les demander avec GetClientRect. GetClientRect (hWnd, &rect); cxClient = rect.right; // toujours en pixels cyClient = rect.bottom; // toujours en pixels Remarque : les rect.top et rect.left renvoyés valent toujours 0. Pour redimension la taille client à x,y : GetWindowRect(hWnd, &rc); MoveWindow(hWnd, rc.left, rc.top, x, y, FALSE); Pour calculer la taille d'une fenêtre en fonction de sa taille client désiré, on utilise : AdjustWindowRect(LPRECT lpRect, // pointer to client-rectangle structure DWORD dwStyle, // window styles BOOL bMenu // menu-present flag ); En entrée on fournit le rectangle client, et en sortie on obtient le rectangle extérieur. Exemple au WM_CREATE : donner à la zone client la dimension MINX, MINY DWORD style, menu ; style = GetWindowLong(hWnd, GWL_STYLE) ; menu = GetWindowLong(hWnd, GWL_ID); rcMin.left = rcMin.top = 0; rcMin.right = MINX; rcMin.bottom = MINY; // Recupère un rectangle augmenté des extras extérieurs AdjustWindowRect(&rcMin, style , menu != 0L); rcMin.right -= rcMin.left ; rcMin.bottom -= rcMin.top ; rcMin.left = rcMin.top = 0 ; MoveWindow(hWnd, 200, 200, rcMin.right, rcMin.bottom, FALSE); Tracer des lignes : ----------------- Par défaut, les lignes sont tracées avec le stylo BLACK_PEN, d'épaisseur 1 pixel. Les fonctions de tracé de lignes utilisent systématiquement les coordonnées logiques. Avant toute modification, la fonction de tracé de ligne la plus élémentaire de (x,y) à (X,Y) : MoveToEx (hdc, x, y, NULL); LineTo(hdc, X, Y) ; trace avec le stylo par défaut (BLACK_PEN) dans le mode de tracé par défaut (R2_COPYPEN) une ligne de la position logique courante par défaut (0,0) jusqu'au point logique (X,Y) qui devient le nouveau point courant. On allume un pixel avec : SetPixel(hdc, X, Y, rgbcolor) ; On déplace le point courant sans tracer de ligne avec : MoveToEx(hdc,X,Y,&pt) ; ou pt reçoit l'ancien point courant (mettre NULL si inutile). GetCurrentPositionEx(hdc, &pt) ; fournit la position courante. Polyline(hdc, &pt, n) ; trace n-1 segments de droite joignant les n points pt. Le point courant reste inchangé. PolylineTo(hdc,&pt,n) ; trace n segments de droite (le 1er joint la position courante à pt[0]). Le dernier point pt[n-1] devient le point courant. PolyBezier(hdc, &pt, n) ; trace (n-1)/3 courbes de Bezier. Si n-1 n'est pas un multiple de 3, rien n'est tracé. Le point courant n'est pas modifié. PolyBezierTo(hdc, &pt, n) ; trace n/3 courbes de Bezier. Si n n'est pas un multiple de 3, rien n'est tracé. Le dernier point pt[n-1] devient le point courant. Changement de stylo : ------------------- On change de stylo en sélectionnant un autre (BLACK_PEN, WHITE_PE et NULL_PEN sont prédéfinis): HPEN hpen, hpold ; hpen = GetStockObject(WHITE_PEN) ; hpold = SelectObject(hdc,hpen) ; // hpold est un handle sur l'ancien stylo. ..... SelectObject(hdc,hpold) ; // On restaure l'ancien stylo. Création de nouveaux stylos : --------------------------- Création d'un stylo logique au sein du GDI par : hpen = CreatePen(iPenStyle, iLargeur, rgbCouleur) ; - iPenStyle est le style : PS_SOLID _____________ PS_DASH _ _ _ _ _ _ _ PS_DOT ............. PS_DASHDOT _._._._._._._ PS_DASHDOTDOT _.._.._.._.._ PS_NULL PS_INSIDEFRAME _____________ - iLargeur est la largeur en unité logiques (0 donne une largeur de 1 pixel) Pour PS_DASH, PS_DOT, PS_DASHDOT et PS_DASHDOTDOT iLargeur ne peut prendre que la valeur 0 (=> 1 pixel). Pour tout autre valeur le style sera remplacé par PS_SOLID. - rgbCouleur est la couleur unie du trait pour tous les styles sauf PS_INSIDEFRAME qui peut être fabriquée par : rgbCouleur = RGB(iR,iG,iB) ; // remarque : rgbCouleur = 0 -> noir. ou iR, iG et iB sont les quantités de rouge, vert et bleu dans l'intervalle 0-255. Cas de PS_DASH, PS_DOT, PS_DASHDOT et PS_DASHDOTDOT : Quand le mode fond est OPAQUE (Cf GetBkMode et SetBkMode), les interstices sont tracés de la couleur du fond (cf GetBkColor, SetBkColor). Si le mode fond est TRANSPARENT, les interstices ne sont pas tracés. Le trait est centré, sauf dans le cas de PS_INSIDEFRAME, pour le tracé de contours, où l'épaisseur du trait est à l'intéreur du rectangle circonscrit. Autre méthode de création de stylo (éventuellement plus rapide en exécution). Elle utilise la structur LOGPEN définie par : typedef struct tagLOGPEN { // lgpn UINT lopnStyle; POINT lopnWidth; // si NULL -> 1 pixel; lopnWidth.y est inutilisé COLORREF lopnColor; } LOGPEN; LOGPEN lgpn = {iPenStyle, {iLargeur,0}, rgbCouleur}; hpen = CreatePenIndirect(&lgpn) ; Creation de stylos plus élaborés : hpen = ExtCreatePen(dwPenStyle, dwWidth, &lplogbrush, dwStyleCount, &lpStyle) ; dwPenStyle : PS_COSMETIC : permet d'ajouter les mêmes possibilités que CreatePen PS_GEOMETRIC : permet d'ajouter des possibilités supplémentaires de traite ment des raccords de segments quand utilisé avec strokepath. dwWith : largeur lplogbrush : le trait est rempli par une brosse en mode PS_GEOMETRIC. En PS_COSMETIC seule la couleur est de la brosse est prise en compte. dwStyleCount : voir doc (mettre à 0) lpStyle : voi doc (mettre à NULL) Utilisation des stylos créés : ---------------------------- Elle se fait comme pour les stylos prédéfinis, par sélection du handle du stylo dans le hdc. Exemple : LOGPEN lgpn = {iPenStyle, {iLargeur,0}, rgbCouleur}; hpen = CreatePenIndirect(&lgpn) ; .... SelectObject(hdc,hpen)) ; ... Utilisation du nouveau stylo DeleteObject(hpen); Les stylos créés doivent être détruits quand ils ne servent plus (au même niveau que leur création, pour éviter que les appels successifs de la procédure ne créent une quantité montrueuse de données). Il ne faut pas détruire un stylo sélectionné. Pour le détruire, soit on en sélectionne un autre, soit on le fait après le EndPaint(hWnd, &ps) ou le ReleaseDC(hWnd, hdc). Exemple : hpold = SelectObject(hdc,CreatePen(PS_DASH,0,RGB(255,0,0)); ..... Utilisation DeleteObject(SelectObject(hdc,hpold)); //Selectionne l'ancien et détruit l'en cours Modes de tracé : -------------- Les tracés résultent d'une opération raster entre 2 motifs (ROP2) : celui préexistant et celui du nouveau tracé. Le type d'opération est lu par : iDrawMode = GetROP2(hdc) et fixé par : SetROP2 (hdc, iDrawMode) ; où les valeurs de iDrawMode les plus utilisées sont : R2COPYPEN trace de la couleur du stylo (défaut) (couleur fond ignorée) R2NOTCOPYPEN trace de la couleur inverse du stylo (couleur fond ignorée) R2BLACK trace en noir (couleur fond ignorée) R2WHITE trace en blanc (couleur fond ignorée) R2_NOT inverse la couleur du fond (couleur stylo ignorée) R2NOP ne trace rien (couleur stylo et fond ignorées) Tracé de rectangles, ellipses, ... : ---------------------------------- Les fonctions suivantes tracent des contours ouverts ou fermés : Rectangle(hdc, Xleft, Ytop, Xright, Ybottom) ; Ellipse (hdc, Xleft, Ytop, Xright, Ybottom) ; RoundRect(hdc, Xleft, Ytop, Xright, Ybottom, dXell, dYell) ; Arc (hdc, Xleft, Ytop, Xright, Ybottom, Xdeb, Ydeb, Xfin, Yfin) ; Chord (hdc, Xleft, Ytop, Xright, Ybottom, Xdeb, Ydeb, Xfin, Yfin) ; Pie (hdc, Xleft, Ytop, Xright, Ybottom, Xdeb, Ydeb, Xfin, Yfin) ; Polygon (hdc, &pt, n) ; PolyPolygon(hdc, &pt, &n, N) - dXell et dYell sont les diamètres des ellipses qui constituent les coins, - Pour les arc, chord (surface limitée par l'arc et la corde) et pie (surface limitée par l'arc et les rayons vecteurs) les extrémités sont à l'intersetction de l'ellipse et des rayons vecteurs passant par les points (Xdeb,Ydeb) et (Xfin,Yfin). Polygon trace un polygone de n ou n-1 cotés suivant que pt[O] et pt[n-1] sont différents ou identiques. PolyPolyGon trace N polygones. Le kième polygone possédant n[k-1] sommets. Changement de brosse (brush) : ----------------------------- Les contours fermés sont remplis avec la brosse par défaut (WHITE_BRUSH). Si on ne veut pas effacer ce qui se trouve derrière, il faut prendre la brosse transparente NULL_BRUSH (== HOLLOW_BRUSH) : hBrush = GetStockObject(NULL_BRUSH) ; // de type hBrush SelectObject(hdc, hBrush) ; Les brosses standards sont : BLACK_BRUSH noire DKGRAY_BRUSH gris foncé GRAY_BRUSH grise HOLLOW_BRUSH transparente LTGRAY_BRUSH gris clair NULL_BRUSH transparente WHITE_BRUSH blanche Remarque : en sélectionnant une brosse quelconque non transparente et le stylo transparent NULL_PEN, on remplit des surfaces sans tracer leur contour. Pour créer une brosse non standard on utilise les fonctions : hBrush = CreateSolidBrush (rgbColor) ; // couleur "unie" hBrush = CreateHatchBrush(iHatchStyle, rgbColor) ; // motif hachuré en couleur hBrush = CreatePatternBrush(hBitmap) ; hBrush = CreateBrushIndirect(&lb) ; où iHatchStyle peut prendre les valeurs suivantes : HS_HORIZONTAL bandes horizontales (pull marin) HS_VERTICAL bandes |||||| HS_FDIAGONAL bandes \\\\\\ HS_BDIAGONAL bandes ////// HS_CROSS quadrillage droit HS_DIAGCROSS quadrillage oblique où lb est de la structure : typedef struct tagLOGBRUSH { // lb UINT lbStyle; COLORREF lbColor; LONG lbHatch; } LOGBRUSH; dont le champ lbStyle détermine l'interprétation des 2 autres : lbStyle lbColor lbHatch ---------------------------------------------- BS_SOLID brosse ignoré => cf CreateSolidBrush (lbColor) BS_HOLLOW ignoré ignoré => cf NULL_BRUSH BS_HATCHED couleur hachures Style hachures => cf CreateHatchBrush(lbHatch,lbColor) BS_PATTERN ignoré handle de bitmap Le fond des motifs hachurés obéit aux mêmes règles que les interstices des lignes. Tout comme pour les stylos, toute brosse crée par un hBrush = Create... doit être détruite par un : DeleteObject(hBrush) ; après EndPaint (hWnd, &ps) ou après qu'une nouvelle brosse ait été sélectionnée. Peinture avec la brosse sélectionnée : PatBlt(hdc,XLeft,YLeft,Width,Height,Raster_operation_code); Accès aux paramètres d'un pinceau ou d'une brosse du hdc : -------------------------------------------------------- Pour accéder à ces paramètres il faut déclarer une structure LOGPEN, ou LOGBRUSH, créer un handle sur cette structure et recopier dans cette structure les attributs du stylo ou de la brosse du hdc : LOGPEN lgpen ; hpen = CreatePenIndirect(&lgpen) ; GetObject(hpen, sizeof(LOGPEN), (void *) &lgpen) ; LOGBRUSH lgbrush ; hBrush = CreateBrushIndirect(&lgbrush) ; GetObject(hpen, sizeof(LOGBRUSH), (void *) &lgbrush) ; Mode de remplissage des polygones : --------------------------------- Les 2 modes de remplissage des polygones sont ALTERNATE (par défaut) et WINDING. Dans ce premier tout l'intérieur est peint. Avec ALTERNATE les zones internes sont alternativement peintes ou pas. On modifie le mode par : iold = SetPolyFillMode(hdc, iMode) ; Remplissage de rectangles : ------------------------- On peut remplir des rectangles sans avoir à sélectionner de brosse dans le hdc par les fonctions : FillRect(hdc, &rect, hBrush) ; Remplit le rectangle avec le motif de hBrush. Les bordures bottom et right sont exclues FrameRect(hdc,&rect,hBrush) ; Ne remplit que le cadre avec une épaisseur d'une unité logique. InvertRect(hdc,&rect) ; // Inversion de tous les pixels. Travail avec des rectangles : --------------------------- SetRect(&rect, xleft,ytop,xright,ybottom) ; // Affectation OffsetRect (&rect,x,y) ; // xleft += x ; xright += x ; ytop += y, ybottom += y ; InflateRect(&rect,x,y) ; // xleft -= x ; xright += x ; ytop -= y, ybottom += y ; SetRectEmpty(&rect) ; // Tout à 0. CopyRect(&DestRect, &ScrRect) ; // DestRect = ScrDest ; IntersectRect(&DestRect, &SrcR1, &SrcR2); UnionRect(&DestRect, &SrcR1, &SrcR2); ib = isRectEmpty(&rect) ; // 1 si left <= right && tp <= bottom ; ib = PtInRect(&rect, pt); // 1 dans le rectangle ou sur left ou top. Régions : ------- Comme les stylos, les brosses et les bitmats, les régions sont des objets du GDI que l'on crée par un CreateXXX et détruit par un DeleteObject. Exemples de création : Rectangulaire : hRgn = CreateRectRgn(xleft,ytop,xright,ybottom) ; hRgn = CreateRectRgnIndirect(&rect) ; Rectangulaire arrondi : hRgn = CreateRoundRectRgn(xleft,ytop,xright,ybottom,dXell,dYell) ; Elliptique : hRgn = CreateEllipticRgn(xleft,ytop,xright,ybottom) ; hRgn = CreateEllipticRgnIndirect(&rect) ; Polygonale : hRgn = CreatePolygonRgn(&pt, n, iFillPolyMode) ; hRgn = CreatePolyPolygonRgn(&pt, &n, N, iFillPolyMode) ; Combinaison des régions : ----------------------- Les régions sont combinées à l'aide de la fonction: iRgnType = CombineRgn(hDestRgn, hSrcRgn1, hSrcRgn2, iCombine) ; hDestRgn est un handle sur la région destination (préexistante qcq). hSrcRgn1 et hSrcRgn2 sont les handles sur les r2gions à combiner suivant la valeur de iCombine. iCombine Opération -------------------------------------------------- RGN_AND DestRgn = hSrcRgn1 & SrcRgn2 RGN_OR DestRgn = hSrcRgn1 | SrcRgn2 RGN_XOR DestRgn = hSrcRgn1 ^ SrcRgn RGN_DIFF DestRgn = hSrcRgn1 - (hSrcRgn1 & SrcRgn) RGN_COPY DestRgn = hSrcRgn1 La valeur iRgnType renvoyée peut être égale à : NULLREGION si vide SIMPLEREGION si rectangle,ellipse ou polygone simple COMPLEXREGION combinaison de simples ERROR erreur quelconque Tracé de région : --------------- PaintRgn(hdc,hRgn) ; // avec la brosse sélectionnée FillRgn (hdc, hRgn, hBrush) ; // Cf FillRect FrameRgn(hdc, hRgn, hBrush, xFrame, yFrame) ; // Cf FrameRect avec en + largeur et hauteur de la bordure InvertRgn(hdc, hRgn); // Cf InvertRect Les Chemins : ----------- BeginPath (hdc) ; // Debut enregistrement MoveToEx, LineTo, etc .. sont enregistrés pour defin un tracé CloseFigure(hdc) ; // permet de fermer un sous chemin EndPath (hdc) ; // Fin enrigistrement StrokePath (hdc) ; // Tracé de l'enregistrement FillPath (hdc) ; // Remplit le chemin avec la brosse StrokeAndFillPath(hdc) ; hRgn = PatToRegion(hdc) ; //Conversion chemin en région SelectClipPath(hdc, iCombine) ; // Voir iCombine a popos des régions. Les Bitmaps adaptés au périf (device context) --------------------------------------------- Ils peuvent etre créés par un des appels suivants : hbitmap = CreateBitmat(Largeur, Hauteur, nbPlans, nbitsParPix, pBufBits); (1) hbitmap = CreateBitmatIndirect(&bitmap); (2) hbitmap = CreateCompatibleBitmap(hdc, largeur, hauteur); (3) hbitmap = CreateDiscardableBitmap(hdc, largeur, hauteur); (obsolete : 4) hbitmap = CreateDIBitmap(hdc, &bmih, 0, NULL, NULL, 0); (5) (1) et (2) ne sont conseillé que pour les bitmap monochrome avec nbPlans = 1 et nbitsParPix = 1. Pour la couleur les valeurs dépendent des modes graphiques. Il est donc déconseillé d'utiliser (1) ou (2) dans ce cas. Pour (3,4,5) les valeurs de nbPlans et nbitsParPix sont obtenues à par les attributs du hdc. Dans (2) la structure bitmap est la suivante : typedef struct tagBITMAP { //bm LONG bmType; // =0 LONG bmWidth; LONG bmHeight; LONG bmWidthBytes; // nb d'octets par ligne (pair) WORD bmPlanes; WORD bmBitsPixel; LPVOID bmBits; } BITMAP; Le nombre de bits du bitmap est égal à : nbits = bm.bmWidthBytes * bm.bmHeight * bmPlanes; Pour récuperer ces params la structure BITMAP s'obtient à partir d'un HBITMAT par : GetObject(hbm, sizeof(bm), &bm); // get pointer to BITMAT bm. Pour copier nbits bits du bitmap dans pBuf, utiliser la fonction : GetBitmapBits(hbm, nbits, pBuf); Inversement pour remplir le bitmap à partir du buffeur, faire : SetBitmapBits(hbm, nbits, pBuf); Les devices contextes en mémoire -------------------------------- Pour dessiner dans un bitmap on crée un "device contexte mémoire" compatible avec le contexte physique (mais de dimension nulle) par : hdcmem = CreateCompatibleDC(hdc); // Si hdc = NULL, il est compatible avec l'écran de l'appli en cours on lui associe la pseudo surface d'affichage du bitmap existant par : SelectObject(hdcmem, hbitmap); Toute écriture dans le hdcmem est effectuée dans le bitmap. Une fois le travail terminé, il faut, comme pour tout dc effacer le hdcmem par : DeleteDC(hdcmem); Affichage d'un bitmap sur le dc ------------------------------- En plus de PatBlt(...) qui permet de peindre une partie de la surface du dc, le transfert de bitmap entre deux dc se fait par la fonction : BitBlt(hdcDest, xDest, yDest, Larg, Haut, hdcSrc, xSrc, ySrc, dwRop); qui prend ds le hdcSrc la portion (xSrc,ySrc,Larg,Haut) pour la plaquer ds le hdcDEst en (xDest,yDest,Larg,Haut) en une operation définie par dwRop qui précise la combinaison effectuée entre les pixels du hdcDest (D), du hdcSrc (S) et de la brosse (P) : Destination seule Destination et Brosse BLACKNESS D = 0 cad noir MERGECOPY D = S & P WHITENESS D = 1 cad blanc SRCPAINT D = S | P DSTINVERT D = !D PATINVERT D = D ^ P PATCOPY D = P Destination et Source (et éventuellement brosse) SRCCOPY D = S NOTSRCCOPY D = !S SRCAND D = D & S SRCINVERT D = D ^ S MERGEPAINT D = D | !S PATPAINT D = D | !S | P SRCERASE D = !D & S NOTSRCERASE D = !(S | D) La fonction suivante permet en plus d'étirer le bitmap : StretchBlt(hdcDest,xDest, yDest, lDest, hDest, hdcSrc, xSrc, ySrc, lSrc, hSrc, dwRop); Ainsi pour afficher un bitmap sur un dc on est obligé de passer par l'intermédiaire d'un autre dc en mémoire. Exemple de routine faisant cela : void DrawBitmap (HDC hdc, HBITMAP hbm, int xDest, int yDest) { BITMAP bm; HDC hdcmem; POINT ptSize, ptOrig; hdcmem = CreateCompatibleDC (hdc); // on cree le dc memoire SelectObject (hdcmem, hbm); // on lui affecte la surface bitmap SetMapMode(hdcmem, GetMapMode(hdc));// on s'assure que les 2 dc ont les memes unites et sens GetObject(hbm, sizeof(bm), &bm); //Pour recuperer les infos sur le bitmap ptSize.x = bm.bmWidth; ptSize.y = bm.bmHeight; DPtoLP(hdc, &ptSize,1); prOrig.x = 0; prOrig.y = 0; DPtoLP(hdcmem, &ptOrig,1); BitBlt(hdc,xDest,yDest, ptSize.x, ptSize.y, hdcmen, ptOrig.x, ptOrig.y, SRCCOPY); DeleteDC(hdcmem); } Les Bitmaps indépendants du périphérique (DIB) : ---------------------------------------------- Structure mémoire contenant un descripteur appelé BITMAPINFO suivi du tableau des bits. Le BITMAPINFO contient lui même un descripteur appelé BITMAPINFOHEADER et éventuellement une table des couleurs. typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; // Le descripteur RGBQUAD bmiColors[?]; // La table des couleurs } BITMAPINFO; La table des couleurs est constituée de structures RGBQUAD : typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; Le descripteur BITMAPINFHEADER contient : typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // Taille en octet de la structure BITMAPINFOHEADER LONG biWidth; // Largeur du DIB en pixels (décompressé) LONG biHeight; // Hauteur du DIB en pixels (décompressé) WORD biPlanes; // = 1 (nb plans de la cible hard) WORD biBitCount; // Nb de bits par pixels (0,1,4,8,16,24,32) DWORD biCompression; // Type de compression (voir ci-après) DWORD biSizeImage; // Taille image en octets (ou 0 si non comprime : BI_RGB) LONG biXPelsPerMeter; // ??? LONG biYPelsPerMeter; // ??? DWORD biClrUsed; // Nb d'entrées RGBQUAD utilisées dans bmiColors DWORD biClrImportant; // Nb d'éntrées RGBQUAD nécessaire au bitmap (0 = toutes) } BITMAPINFOHEADER; Si biHeight>0 lignes basses en premier, sinon (biHeight<0) lignes hautes en premier. Les DIB inverses (biHeight<0) ne peuvent pas être comprimés. biCompression : BI_RGB Non comprimé BI_RLE8 Compression RLE 8 bits/pixel -> paires d'octets (nb , coul_idx) BI_RLE4 Compression RLE 4 bits/pixel -> paires d'octets (nb, (coul_idx, coul_idx)) BI_BITFIELS Non comprimé : table de couleur de 3 masques DWORD (3x32bits) BI_JPEG Image JPEG Si biBitCount vaut : 0 c'est le format JPEG qui spécifie sa vraie valeur 1 DIB monochrome (0->couleur RGBQUAD[0], 1->couleur RGBQUAD[1]) 4 couleur <- RGBQUAD[pix] : table de 2**4 = 16 entrées RGBQUAD 8 couleur <- RGBQUAD[pix] : table de 2**8 = 256 entrées RGBQUAD 16 couleur RGB avec 5 bits par pixels éventuellement Si BI_RGB -> table de biClrUsed entrées RGBQUAD Si BI_BITFIELDS table de couleur de 3 masques DWORD (3x32bits) 24 couleur RGB à 3 octets. Généralement pas de table (bmicolors : NULL) ou table de biClrUsed entrées RGBQUAD pour optimiser la palette. 32 couleur RGB à 4 octets (3 utilisés). Si BI_RGB : Généralement pas de table (bmicolors : NULL) ou table de biClrUsed entrées RGBQUAD pour optimiser la palette. Si BI_BITFIELDS : palette de 3 masques 32bits Adresse de la table de couleur : biSize après le début du BITMAPINFO Taille de la table de couleur : biClrUsed*sizeof(RGBQUAD) Si biBitCount == 24 => biClrUsed = 0 : pas de table de couleur Si biBitCount == 16 ou 32 => biClrUsed = 3*sizeof(DWORD) ?? Si biBitCount < 8 => biClrUsed = 1 << biBitCount Adresse tableau des Pixels : Juste après la table des couleurs Taille du tableau : Nombre de bits par pixels : biSizeImage = biHeight*(4*(((DWORD)biWidth*biBitCount + 31)/32) biSizeImage = biHeight*((((DWORD)biWidth*biBitCount + 31)& 0xffffffe0) >> 3) Affichage du BITMAPINFOHEADER : (d'adresse lpbi) ptext = texte ; ptext += sprintf(ptext,"Nbits par pixel = %d\n",lpbi->biBitCount) ; ptext += sprintf(ptext,"Nb idx ? = %d\n",lpbi->biClrImportant) ; ptext += sprintf(ptext,"Nb idx table couleur = %d\n",lpbi->biClrUsed) ; ptext += sprintf(ptext,"Compression = %d\n",lpbi->biCompression) ; ptext += sprintf(ptext,"Hauteur bitmap = %d\n",lpbi->biHeight) ; ptext += sprintf(ptext,"Np plans cible = %d\n",lpbi->biPlanes) ; ptext += sprintf(ptext,"Taille structure = %d\n",lpbi->biSize) ; ptext += sprintf(ptext,"Taille image = %d\n",lpbi->biSizeImage) ; ptext += sprintf(ptext,"Largeur bitmap = %d\n",lpbi->biWidth) ; ptext += sprintf(ptext,"Pixels par metre X = %d\n",lpbi->biXPelsPerMeter) ; ptext += sprintf(ptext,"Pixels par metre Y = %d\n",lpbi->biYPelsPerMeter) ; MessageBox(NULL,texte,"Affiche",MB_OK); Affichage à l'écran du DIB : pBits = ((LPSTR)lpbi + (WORD)(lpbi->biSize) + lpbi->biClrUsed * sizeof(RGBQUAD)) ; // Affichage d'une portion dans le rectangle [xh, yh, dx, dy] SetDIBitsToDevice(hdc, xh, yh, dx, dy,0,0,0, lpbi->biHeight, pBits, (BITMAPINFO *) lpbi, DIB_RGB_COLORS); // Affichage avec adaptation de taille dans le rectangle [xh, yh, dx, dy] StretchDIBits(hdc, xh, yh, dx, dy,0,0,lpbi->biWidth, lpbi->biHeight, pBits, (BITMAPINFO *) lpbi, DIB_RGB_COLORS, SRCCOPY); Valider ou invalider des zones : ------------------------------ De manière automatique, dès que, suite à une manipulation de fenêtre, une zone est à retracer, Windows la met dans le rectangle invalide et poste le message WM_PAINT dans la file des messages. Le programmeur peut également définir des zones invalides qui sont ajoutées aux zones à redessiner, ou au contraire valider des zones qui sont enlevées de la zone à redessiner. S'il existe une zone non vide à redessiner, Windows génère un message WM_PAINT pour l'appli si sa file des messages est vide. Les unités utilisées pour ces zones sont ici des unités physiques relatives à la zone client. InvalidateRect(hWnd, &rect, bErase); Ajoute le rectangle rect à la région à invalider de la fenêtre hWnd. Si bERASE est TRUE, le fond sera effacé par BeginPaint (et donc à retracer). Si hWnd est NULL, toutes les fenêtres de l'appli sont invalidées et retracées. Si &rect est NULL toute la zone client est invalidée. InvalidateRgn(hWnd, hRgn, bErase); idem avec une région à la place d'un rectangle. Ex : InvalidateRect(hWnd,NULL,TRUE) et InvalidateRgn(hWnd,NULL,TRUE) produisent l'effacement de toute la zone client (équivalent à clearscreen). ValidateRect(hWnd, &rect); a) Valide le rectangle rect dans la région à invalider de la fenêtre hWnd. b) Si &rect est NULL toute la zone client est validée. c) Si hWnd est NULL, toutes les fenêtres de l'appli sont invalidées et retracées. ValidateRgn(hWnd, hRgn); idem avec une région à la place d'un rectangle. Ex : ValidateRect(hWnd, NULL) ou ValidateRgn(hWnd, NULL) peuvent être utilisés pour empêcher l'envoi de WM_PAINT. ValidateRect(NULL, &rect) produira le rafraichissement de toutes les fenêtres de l'appli. GetUpdateRect(hWnd, &rect, bErase); Recupère le plus petit rectangle contenant toute la région invalide de la fenêtre hWnd et spécifie si le fond est à effacer(bERASE à TRUE). Si &rect est passé à NULL, le BOOL renvoyé vaut 0 s'il n'y a pas de région invalide et !0 s'il y en a une. GetUpdateRgn(hWnd, hRgn, bErase); Idem, mais récupère la région invalide exacte. Dans le traitement de WM_PAINT, le rectangle invalide est accessible dans la structure ps (de type PAINSTRUCT) par le champ rcPaint (de type RECT) : ps.rcPaint. Le motif utilisé pour effacer le fond est celui défini lors de l'enregistrement de la classe fenêtre : wndclass.hbrBackground = (hBrush) GetStockObject (WHITE_BRUSH) ; Modifier la zone de clipping : ---------------------------- Entre BeginPaint et EndPaint, on ne peut dessiner que dans la zone invalide. Tout ce qui est tracé à l'extérieur est ignoré. On peut modifier cela de diverses manières en agissant sur les régions valides ou invalides, mais également en fixant soi-même la région de clipping au moyen de : SelectClipRgn (hdc, hRgn) ; Si Hrgn est NULL => raz clipping SelectObject (hdc, hRgn) ; // synonyme du précédent. ExtSelectClipRgn(hdc, hRgn, iCombine) ; Si Hrgn est NULL => raz clipping iCombine spécifie l'opération (Cf CombineRgn) entre hRgn et la zone courante de clipping. ExcludeClipRect(hdc,l,t,r,b) ; Enlève le rectangle ltrb de la zone de clipping (on peut tracer à nouveau dans ce rectangle). IntersectClipRect(hdc,l,t,r,b) ; La zone de clipping devient l'intersectioin de rectangle ltrb avec l'ancienne zone OffsetClipRgn(hdc,dx,dy) ; Déplace la région de clipping de dx vers la droite et dy vers le bas. Entre GetDC et ReleaseDC, il n'y a pas de zone de clipping. On ne peut qu'interdire les tracés dans la région invalide (puisque ceux-ci seront effectués lors du traitement de WM_PAINT) par un appel à ExcludeUpdateRgn(hdc, hWnd). Ecriture de texte : ----------------- La fonction la plus élémentaire qui permet d'écrire du texte est : BOOL TextOut(HDC hdc,int X,int Y,char *pString,int nCar); Ecrit dans le hdc, à l'emplacement (X,Y) nCar caractères de la chaîne pointée par pString. X,Y sont exprimés en unités logiques. Les caractères de commande (saut de ligne etc...) ne sont pas interprétés. Les caractéristiques de l'apparence du texte écrit sont des attributs du DC : Attributs Défaut Pour lire Pour changer ------------------------------------------------------------------------ Police (Font) SYSTEM_FONT SelectObject SelectObject Mode Fond OPAQUE GetBkMode SetBkMode Couleur Fond Blanche GetBkColor SetBkColor Couleur Texte Noire GetTextColor SetTextColor Espacement 0 GetTextCharacterExtra SetTextCharacterExtra Exemple de changement de Police : SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)) ; Les polices prédéfinies sont : ANSI_FIXED_FONT Police système fixe Windows 95. ANSI_VAR_FONT Police système proportionnelle Windows 95 DEFAULT_GUI_FONT Police des menus et boîtes de dialogue par défaut. OEM_FIXED_FONT Police fixe (jeu de caractères étendu IBM pour DOS). SYSTEM_FONT Police système proportionnelle Windows >= 3.0 SYSTEM_FIXED_FONT Police système fixe Windows < 3.0 Accés aux informations sur la police : GetTextMetrics(hdc, &tm) ; ou tm est une structure TEXTMETRIC dont les principaux champs sont : tm.tmHeight Hauteur totale caractère (jambe et accent compris) tm.tmAscent Hauteur totale au dessus de la ligne de base tm.tmInternalLeading Hauteur accent tm.tmExternalLeading Hauteur interligne (placé au-dessus). tm.tmAveCharWidth Largeur moyenne d'un caractère minuscule(le x) tm.tmMaxCharWidth Largeur max d'un caractère (le M) tm.tmPitchAndFamilly Divers. Bit de poids faible (0/1 -> police fixe/variable) Aisi la hauteur de la jambe du p est de "tm.tmHeight-tm.tmAscent", la hauteur d'un A majuscule, sans accent est de "tm.Ascent-tm.tmInternalLeading", la hauteur totale d'une ligne vaut "tm.tmHeight+tm.tmExternalLeadint", interligne comprise. La largeur moyemme d'une majuscule peut être estimée par : tm.AveCharWidth*(tm.tmPitchAndFamilly&1:3:2)/2 ; c'est-à-dire 1.5 fois ou identique au "x" moyen suivant que la police est variable ou fixe. On peut également obtenir les dimensions moyennes de la police système par: cxChar = LOWORD(GetDialogBaseUnits()); // largeur moyenne caractère de SYSTEM_FONT cyChar = HIWORD(GetDialogBaseUnits()); // hauteur police SYSTEM_FONT La position effective du texte écrit par TextOut, par rapport au point de référence (X,Y donnés dans TextOut par defaut) dépend du mode TextAlign (TA) fixé par SetTextAlign et lu par GetTextAlign. Par défaut (modes TA_TOP, TA_LEFT) le point haut à gauche du 1er caractère est positionné au point de référence. Lorsque le mode TA_UPDATECP est mis, le point courant (positionné par MoveToEx, LineTo..) est mis-à-jour à chaque nouveau TextOut (X,Y sont ignorés) et c'est ce point courant qui sert de point de référence pour les TextOut suivants. Par défaut (mode TA_NOUPDATEDC) c'est le point (X,Y donnés dans TextOut) qui est utilisé et le point courant n'est pas modifié. UINT SetTextAlign(HDC hdc,UINT fMode); fMode peut prendre les valeurs suivantes : TA_BASELINE point-ref sur la ligne de base TA_BOTTOM point-ref sur la ligne inférieure TA_TOP point-ref sur la ligne supérieure TA_CENTER abscisse point_ref au centre du texte TA_LEFT abscisse point_ref à gauche du texte TA_RIGHT abscisse point_ref à droite du texte TA_NOUPDATECP x,y transmis par TextOut sont utilisés comme point-ref. Le point courant n'est pas actualisé. TA_RTLREADING (Windows 95) only: Texte écrit de droite à gauche avec les fontes Hebrew ou Arabic. TA_UPDATECP La position courante est utilisée comme point-ref. Elle est actualisée après le tracé. Les modes par défaut sont : TA_LEFT, TA_TOP, et TA_NOUPDATECP. Par exemple, pour afficher des nombres alignés a droite on choisira : SetTextAlign(hdc,TA_RIGHT|TA_TOP) en fournissant comme coordonnées dans TexOut la position du dernier chiffre à écrire. Taille exacte du texte écrit : ---------------------------- On peut l'obtenir dans une structure SIZE par la fonction : GetTextExtentPoint32(hdc,pTexte,strlen(pTexte), &siz) ; Texte avec des tabs : ------------------- TabbedTextOut (hdc, posX, posY, pTexte, nCar, nTabs, pTabstop, xTabOrigin) ; hdc, posX, posY, pTexte, nCar : idem TextOut pTabstops : tableau des valeurs des abscisses en pixels des nTabs xTabOrigin : abscisse logique de départ des tabulations si nTabs = 0 et pTabstops = NULL => tabs tous les 8 caractères si nTabs = 1, tabs equidistants de largeur pTabstops[0]. Texte formatté : -------------- DrawText(hdc, pTexte, nCar, &rect, iFormat) ; nCar = -1 -> la chaine pTexte doit être terminée par un zéro. iFormat : 0 Les lignes séparées par des '\r' (0x0D) ou de '\n' (0x0A) sont écrites dans le rectangle rect (Externalleading n'est pas pris en compte). Si les lignes dépassent la largeur du rect, elles sont tronquées. DT_WORDBREAK Retour à la ligne à la fin d'un mot pour les lignes trop longues. DT_EXTERNALLEADING pour le prendre en compte DT_EXPANDTABS tous le 8 caractères DT_NOCLIP Pas de clipping (rect.bottom et rect.right sont ignorés) DT_LEFT Alignement à gauche (par défaut) DT_RIGHT Alignement à droite DT_CENTER Centrage horizontal DT_SINGLELINE Les '\r' (0x0D) et '\n' (0x0A) ne sont pas interprètés mais affichés (pavé ?). DT_TOP Ligne en haut du rectangle (suppose DT_SINGLELINE) DT_BOTTOM Ligne en bas du rectangle (suppose DT_SINGLELINE) DT_VCENTER Ligne au milieu vertical du rectangle (suppose DT_SINGLELINE) Exemple simple d'écriture de texte multilignes : ---------------------------------------------- GetClientRect (hWnd, &rect) ; DrawText(hdc, Texte,-1,&rect,DT_WORDBREAK|DT_EXTERNALLEADING|DT_EXPANDTABS); Les barres de défilement : ------------------------ Pour les inclure il faut ajouter le style WS_VSCROLL (pour la verticale) et/ou WS_HSCROLL (pour l'horizontale) en 3ième paramètre de l'appel de CreateWindow(...). Il faut ensuite fixer la dynamique affectée à l'intervalle total par SetScrollRange, dans le traitement de WM_CREATE a priori. Ainsi : SetScrollRange(hWnd, SB_VERT, ymin, ymax,FALSE) ; SetScrollRange(hWnd, SB_HORZ, xmin, xmax, TRUE) ; fixe la dynamique ascenseur vertical entre ymin et ymax et ne redessine pas l'ascenseur(FALSE), et fixe la dynamique du tapis roulant horizontal entre xmin et xmax et le redessine (TRUE) en fonction de ce nouvel intervalle. L'intervalle entier max-min peut être quelconque (quelques unités ou assez grand. Par défaut (si pas d'appel à SetScrollRange) min=0 et max=100. Exemple : si la taille fenêtre dyWin est < à la taille du dessin dyImag, peut utiliser l'ascenseur suivant pour voir toute l'image : SetScrollRange(hWnd, SB_VERT, 0, dyImag-dyWin, TRUE); y = GetScrollPos(hWnd, SB_VERT) ; // Donne la hauteur cachée au-dessus de la fenêtre Remarque : SetScrollRange(hWnd, SB_VERT, y, y, TRUE); // (y==y) => ascenseur caché Il faut ensuite gérer le curseur en fonction des actions de l'utilisateur. On modifie la position du curseur au moyen de SetScrollPos. A titre d'exemple : SetScrollPos(hWnd, SB_VERT, ipos, TRUE) ; positionne le curseur vertical (car SB_VERT) à la position entière ipos (ymin<=ipos<=ymax) et redessine (car TRUE) le curseur à sa nouvelle position. Remarque: Plus difficile à utiliser SetScrollInfo(hWnd, SB_XXX, &sructscr, bRedraw) ; // Fixe tout En général SetScrollRange et SetScrollPos doivent être modifiés lors de la réception d'un WM_SIZE. Pour gérer le curseur, il faut traiter les messages WM_VSCROLL et/ou WM_HSCROLL. Pour cela, LOWORD(wParam) indique l'action de la souris sur la barre. Les principales actions à traiter sont : case WM_VSCROLL: switch(LOWORD(wParam)) { case SB_TOP: // curseur désiré en haut (fenêtre pricipale uniquement) case SB_BOTTOM: // curseur désiré en bas (fenêtre pricipale uniquement) case SB_LINEUP: // curseur désiré -1 ligne case SB_LINEDOWN: // curseur désiré +1 ligne case SB_PAGEUP: // curseur désiré -1 page case SB_PAGEDOWN: // curseur désiré +1 page case SB_THUMBTRACK: // ascenseur en cours de traction. (HIWORD(wParam)) contient // la position courante désirée. case SB_THUMBPOSITION:// fin traction ascenseur. (HIWORD(wParam)) contient la // position finale désirée. } .... return 0; case WM_HSCROLL: switch(LOWORD(wParam)) { case SB_LEFT: // extrémité gauche case SB_RIGHT: // extrémité droite case SB_LINELEFT: // un pas à gauche case SB_LINERIGHT: // un pas à droite case SB_PAGELEFT: // un page à gauche case SB_PAGERIGHT: // un page à droite case SB_THUMBTRACK: // curseur en cours de traction. (HIWORD(wParam)) contient // la position courante désirée. case SB_THUMBPOSITION:// fin traction curseur. (HIWORD(wParam)) contient la // position finale désirée. } .... return 0; c'est le programmeur qui attribue une taille à la ligne, à la page, au petit et au grand pas. SB_TOP, SB_BOTTOM, SB_LINEUP, SB_LINEDOWN, SB_PAGEUP et SB_PAGEDOWN sont envoyés quand la souris est appuyée. LOWORD(wParam) prend ensuite la valeur SB_ENDSCROLL (bouton souris relaché) qui peut être ignoré. SB_THUMBTRACK peut être ignoré si on désire ne faire évoluer les choses qu'à la fin de la traction du curseur. Pour déplacer instantanément une zone rectangulaire on peut faire appel à : ScrollWindow (hWnd, deltax, deltay, pRect, pClip) ; qui déplace le rectangle pointé par pRect (toute la zone client si NULL) de deltax, deltay et ne dessine rien à l'extérieur du rectangle pointé par pClip (toute la zone client si NULL). Si on veut demander l'effacement de toute la fenêtre on fera appel à : InvalidateRect(hWnd,NULL,TRUE); //Tout effacer et pour demander la mise à jour immédiate de la fenêtre on fera : UpdateWindow(hWnd); //Redessiner Pour utilisation voir SYSMETS2 et SYSMETS3 dans Petzold chapitre 3. DIVERS : ------ LockWindowUpdate(hWnd) ; // page 223 Bloque les tracés dans la fenêtre hWnd. Si hWnd == NULL les tracés sont à nouveau autorisés dans la fenêtre bloquée. Renvoi TRUE si réussite. GetDesktopWindow() ; // page 223 Récupère le hWnd de la fenêtre écran (le bureau). CLAVIER : ------- L'appui sur une touche du clavier provoque l'envoi des messages suivants à la fenêtre: WM_SYSKEYDOWN si touche système (ALT, etc...) appuyée WM_SYSKETUP si touche système (ALT, etc...) relachée WM_KEYDOWN si touche ordinaire appuyée WM_KEYUP si touche ordinaire relachée lParam contient: - un compteur de répétition de la touche - le code de balayge clavier OEM sur 8 bits - le flag de touche étendue - le code de contexte (1 si ALT) - l'état antérieur de la touche (1 si déjà enfoncée) - l'état transitoire de la touche (1 vient d'être relachée) wParam contient le code virtuel de la touche dont les noms sont définis dans windows.h (voir Petzold p281) Pour savoir si CTRL et MAJ sont appuyées il faut utiliser la fonction: GetKeyState(VK_SHIFT) ; <0 si MAJ appuyée GetKeyState(VK_CAPITAL)&1 ; 1 si VERROUILLAGE-MAJ On peut également utiliser : (GetAsyncKeyState(key) & 0x8000) -> TRUE si appuyé (GetAsyncKeyState(key) & 0x0001) -> TRUE si a été appuyé depuis le dernier appel de GetAsyncKeyState. Généralement on laisse à Windows le soin de traiter WM_SYSKEYDOWN, WM_SYSKETUP, et WM_KEYUP. ET ON NE TRAITE WM_KEYDOWN QUE POUR GERER LES MOUVEMENTS CURSEURS. wParam Action souris équivalente VK_HOME SendMessage(hWnd,WM_VSCROLL, SB_TOP,0L) ; VK_END SendMessage(hWnd,WM_VSCROLL, SB_BOTTOM,0L) ; VK_PRIOR SendMessage(hWnd,WM_VSCROLL, SB_PAGEUP,0L) ; VK_NEXT SendMessage(hWnd,WM_VSCROLL, SB_PAGEDOWN,0L) ; VK_UP SendMessage(hWnd,WM_VSCROLL, SB_LINEUP,0L) ; VK_DOWN SendMessage(hWnd,WM_VSCROLL, SB_LINEDOWN,0L) ; VK_LEFT SendMessage(hWnd,WM_HSCROLL, SB_PAGEUP,0L) ; VK_RIGHT SendMessage(hWnd,WM_HSCROLL, SB_PAGEDOWN,0L) ; Tableau des touches virtuelles: Déc Windows Clavier 1 VK_LBUTTON 2 VK_RBUTTON 3 VK_CANCEL CTRL+BREAK 4 VK_MBUTTON 8 VK_BACK BACKSPACE 9 VK_TAB TAB 12 VK_CLEAR 5_NUM (avec NUM éteint) 13 VK_RETURN ENTREE 16 VK_SHIFT MAJUSCULE 17 VK_CONTROL CTRL 18 VK_MENU ALT 19 VK_PAUSE PAUSE 20 VK_CAPITAL VERROU MAJ. 27 VK_ESCAPE ESCAPE 32 VK_SPACE Barre Espace 33 VK_PRIOR PAGE PREC 34 VK_NEXT PAGE SUIV 35 VK_END FIN 36 VK_HOME DEBUT 37 VK_LEFT Flèche <- 38 VK_UP Flèche ^ 39 VK_RIGHT Flèche -> 40 VK_DOWN Flèche V 41 VK_SELECT 42 VK_PRINT 43 VK_EXECUTE 44 VK_SNAPSHOT IMPR ECRAN 45 VK_INSERT INSERT 46 VK_DELETE DELETE 47 VK_HELP 48-57 0 à 9 clavier principal 65-90 A à Z 96 VK_NUMPAD0 0_NUM (avec NUM allumé) jusqu'à 105 VK_NUMPAD9 9_NUM (avec NUM allumé) 106 VK_MULTIPLY *_NUM 107 VK_ADD +_NUM 108 VK_SEPARATOR 109 VK_SUBSTRACT -_NUM 110 VK_DECIMAL ._NUM 111 VK_DIVIDE /_NUM 112 VK_F1 F1 jusqu'à 127 VK_F16 F16 144 VK_NUMLOCK VERR NUM 145 VK_SCROLL ARRET DEFIL Après réception de WM_SYSKEYDOWN et WM_KEYDOWN TranslateMessage poste des messages WM_CHAR, WM_SYSCHAR, WM_DEADCHAR et WM_SYSDEADCHAR qui s'intercallent avant la réception de WM_SYSKETUP, et WM_KEYUP. Pour traiter les caractères IL SUFFIT DE TRAITER WM_CHAR. WM_CHAR: wParam contient le code ASCII des caractères alphanumériques et des caractères de contrôle usuels (CTRL+A -> 01h, ..., CTRL+Z -> 1Ah, CTRL+Enter et CTRL+J -> 0Ah, Enter et CTRL+M -> ODh, Esc -> 1Bh) lParam idem message origine. Remarque 1: l'appel t= GetMessageTime(); donne à quel instant à eu lieu le dernier message fournit par GetMessage. Cette fonction permet par différence de calculer des durées (d'appui d'une touche par exemple) Remarque 2: Pour bloquer les actions des touches systèmes faire : case WM_SYSKEWDOWM : case WM_SYSKEWUP : case WM_SYSCHAR : return 0 ; LES JEUX DE CARACTERES : ---------------------- Windows utilise le jeu de caractère ANSI codé sur 8 bits (idem ascii et OEM pour 20h <= x <= 7Eh) Pour afficher le jeu OEM, il y a intérêt à utiliser la police OEM_FIXED_FONT : SelectObject(hdc,GetStockObject(OEM_FIXED_FONT)); mais les caractères lu au clavier sont fournis en ANSI, il y a intérêt à les afficher avec la police ANSI SYSTEM_FONT par exemple. Pour convertir des chaines il faut utiliser les fonctions Windows: CharUpper(pString); // terminé par zéro. CharLower(pString); CharUpperBuff(pString,nLength); CharLowerBuff(pString,nLength); OemToChar(pStrOem, pStrAnsi); CharToOem(pStrAnsi, pStrOem); OemToCharBuff(pStrOem, pStrAnsi,nDestLength); CharToOemBuff(pStrAnsi, pStrOem,nDestLength); Pour convertir un seul caractère : ch = CharUpper((PSTR) (LONG) (BYTE) ch) ; LE FOCUS: -------- Les entrées clavier vont à la fenêtre qui possède le focus d'entrée clavier. On donne le focus d'entrée à une fenêtre en cliquant dedans avec la souris. Dans certains cas, la touche TAB permet de faire progresser le focus de fenêtre en fenêtre. Pour savoir si une fenêtre possède le focus d'entrée elle peut utiliser la fonction GetFocus() qui renvoie le handle de la fenêtre qui le possède : if(hWnd == GetFocus()) { // La fenêtre a le focus ... } Pour attribuer par programme le focus à la fenêtre hWnd on peut faire: SetFocus(hWnd); LE CARET : -------- Le caret est une ressource système (il n'y en a qu'un) que l'on peut faire apparaitre dans la fenêtre qui possède le focus d'entrée. 1) La fenêtre crée sa représentation du caret en traitant le message WM_SETFOCUS: case WM_SETFOCUS: CreateCaret(hWnd,NUKK,cxChar,cyChar); SetCaretPos(xpos*cxChar,ypos*cyChar); ShowCaret(hWnd); return 0; 2) Elle la détruit lorsqu'elle perrd le focus: case WM_KILLFOCUS: HideCaret(hWnd); DestroyCaret(); return 0; 3) Avant tout tracé effectué hors du traitement de WM_PAINT, il faut cacher le caret par HideCaret, puis le réafficher ensuite par ShowCaret, après l'avoir éventuellement repositionné par SetCaretPos. Remarque : La fenêtre active peut recevoir certain messages (par exemple WM_SIZE) sans avoir le focus d'entrée qui peut appartenir à une de ses fenêtres enfants. LA SOURIS : --------- Existence d'une souris ? : fMouse = GetSystemMetrics(SM_MOUSEPRESENT) ; // -> TRUE si présente Nombre de boutons ? : cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS) ; // 0 si pas de souris Changer de curseur : (IDC_ARROW, IDC_CROSS, IDC_WAIT, ...) hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)) ; sélectionne le sablier. hOldCursor repère l'ancien. ShowCursor(TRUE/FALSE) ; Autorise/interdit l'affichage du curseur. En fait incrémente ou décrémente le compteur d'affichage. Le curseur est affiché si le compteur est >= 0. Le compteur est initialisé à 0 quand il y a une souris et a -1 quand il y en a pas. Où est la souris: GetCursorPos(pPoint); donne la position de la souis en coordonnées écran (voir ScreenToClient). Déplacer la souris: SetCursorPos(x, y); ou x et y sont en coordonnées écran (voir ClientToScreen). Les messages reçus par la fenêtre client : WM_MOUSEMOVE, WM_xBUTTONDOWN, WM_xBUTTONUP, WM_xBUTTONDBLCLK avec pour x : L(left), R(right) ou M(middle) quand il est présent. lParam contient (y,x) par rapport au coin haut-gauche et wParam l'état des boutons et des touches CTRL et SHIFT (codés avec des masques MK_xxxxx) : y = HIWORD(lParam) ; x = LOWORD(lParam) ; wParam contient l'état des boutons et touches SHIFT et CTRL: wParam & MK_xBUTTON -> bouton x (L,R ou M) appuyé wParam & MK_SHIFT -> touche SHIFT appuyée wParam & MK_CONTROL -> touche CTRL appuyée On peut également utiliser GetKeyState pour connaitre ces états, avec les codes VK_SHIFT, VK_CONTROL et VK_xBUTTON (x=L,R,M). Enfoncé si GetKeyState(VK_...) <0. Les doubles clics (messages WM_xBUTTONDBLCLK) ne sont reçus par la fenêtre que si on l'a dotée du style de classe CS_DBLCLKS (avec CS_HREDRAW, etc...) avant d'appeler RegisterClassEx. La fenêtre reçoit àlors dans l'ordre : WM_xBUTTONDOWN WM_xBUTTONUP WM_xBUTTONDBLCLK WM_xBUTTONUP on voit que WM_xBUTTONDBLCLK prend la place d'un WM_xBUTTONDOWN. Pour utiliser proprement cette structure, il y a intérêt à ce que le traitement du simple clic soit indépendant du fait qu'il soit ou non un double clic. Ex : le simple clic ne fait que sélectionner et le double exécute une action sur l'objet sélectionné. Lorsque la souris est dans la fenêtre active, hors zone client, elle transmet des messages que l'on peut ignorer: WM_NCLxBUTTONDOWN,WM_NCLxBUTTONUP,WM_NCLxBUTTONDBLCLK. lParam contient (y,x) en coordonnées écran et wParam indique la zone non client concernée. Remarque : Blocage des messages souris pour la fenêtre. En fait tous les messages souris sont des traductions d'un message original qu'on ne traite pas : WM_NCHITTEST. En interceptant ce message on peut annihiler les actions souris pour toute la fenêtre et uniquement pour elle : case WM_NCHITTEST : return (LRESULT) HTNOWHERE ; // souris sur aucune fenêtre Pour simuler un message souris, suite à une sélection effectuée au clavier, on pourra par exemple envoyer le message suivant: SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(x,y)); MAKELONG(loword, hiword) permet de fabriquer un WPARAM ou un LPARAM à partir de leurs 2 mots. CAPTURE DE LA SOURIS: -------------------- Pour continuer à recevoir les messages de la souris quand elle est en dehors de la fenêtre, on capture la souris par le message suivant: SetCapture(hWnd); placé a priori dans le traitement de WM_xBUTTONDOWN On la libère par: ReleaseCapture(); placé partout où se termine le traitement initié à la capture, cad traitement de WM_xBUTTONUP, etc... CREATION DE FENETRES QUELCONQUES : -------------------------------- Si on désire créer une fenêtre d'une classe autre que celles prédéfinies par Windows, il faut d'abord enregistrer sa classe. Pour cela on définit cette classe en déclarant la structure: WNDCLASSEX wndclass ; Les champs sont remplis comme suit: wndclass.cbSize // taille de la structure = sizeof (wndclass); wndclass.style // style de la classe = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc // nom de la procédure associée à cette fenêtre = WndProc; wndclass.cbClsExtra // nb d'octets à tout faire, allouées pour la classe = 0 ; wndclass.cbWndExtra // nb d'octets à tout faire alloués à chaque fenêtre crée = 0 ; wndclass.hInstance // handle de l'instance de la fenêtre principale = hInstance ; wndclass.hIcon // handle de l'icone qui sera associée à la fenêtre = LoadIcon (NULL, IDI_APPLICATION) ; // NULL pour une CHILDWINDOW wndclass.hCursor // handle sur le curseur souris utilisé sur la fenêtre = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground // handle sur la brosse utilisée pour le fond = (HBRUSH) GetStockObject (WHITE_BRUSH) ; // = CreateSolidBrush(OL) ; Exemple de fond noir. A deleter lors de WM_DESTROY. // = (HBRUSH) (COLOR_WINDOW+1); Fond de fenêtre standard. wndclass.lpszMenuName // Nom du menu dans le fichier ressource = NULL ; wndclass.lpszClassName // Nom attribué à la classe ainsi crée = "MaClasse" ; wndclass.hIconSm // Petite icone pour W95 (NULL si CHILWINDOW). On peut utiliser // le même nom que pour .hIcon. La réduction sera automatique. = LoadIcon (NULL, IDI_APPLICATION) ; Ensuite on enregistre cette structure par: RegisterClassEx(&wndclass); CREATION DE FENETRE QUELCONQUE ENREGISTREE ou PREDEFINIE : -------------------------------------------------------- Ensuite on peut créer une fenêtre de cette classe: CreateWindow("MaClasse" // Nom précédemment attribué à la classe ,"Texte fenêtre" // Nom qui apparaitra en titre ou texte intérieur (ou NULL) ,WS_OVERLAPPEDWINDOW // Style de la fenetre ,PosX,PosY // Position initiale coin haut-gauche ,largeur,hauteur // largeur, hauteur ,NULL // NULL ou handle fenêtre parent si fenêtre enfant ,NULL // NULL ou handle menu ou (HMENU) ID du numéro attribué à fenêtre enfant ,hinstance // handle instance fenêtre principale ,NULL); // pointeur sur structure de création à passer à la WndProc. Les WindowsStyle : WS_OVERLAPPED // Fenêtre ordinaire qui peut être n'importe où et éventuellement cachée WS_POPUP // Fenêtre popup (sans bordure, elle peut occuper tout l'écran. // Nécessite ALT-F4 pour la tuer, si rien d'autre de programmé) WS_CHILD // Fenêtre enfant toujours à l'intérieur et au-dessus de sa mère WS_VISIBLE // Initialement visible WS_DISABLED // Initialement invisible WS_MAXIMIZE // Initialement à la taille maximale (du parent ou de l'écran) WS_MINIMIZE // Initialement iconisée WS_BORDER // Avec une bordure simple WS_DLGFRAME // Avec une bordure double WS_CAPTION // Avec barre de titre. Fenêtre déplaçable (= WS_DLGFRAME|WS_BORDER) WS_THICKFRAME // Avec cadre que l'on peut redimensionner WS_SYSMENU // Avec menu système (suppose WS_CAPTION) WS_MINIMIZEBOX // Boîte minimisation si WS_CAPTION et WS_SYSMENU. WS_MAXIMIZEBOX // Taille maximale ou minimale par dbleclic dans WS_CAPTION, et boîte maximisation si WS_SYSMENU WS_OVERLAPPEDWINDOW //OVERLAPPED+CAPTION+SYSMENU+THICKFRAME+MINIMIZEBOX+WS_MAXIMIZEBOX WS_POPUPWINDOW //BORDER+POPUP+SYSMENU (nécessite WS_CAPTION) WS_CLIPCHILDREN // Protège de l'écriture par la mère WS_CLIPSIBLINGS // Protège de l'écriture par autre filles WS_HSCROLL // Avec tapis roulant WS_VSCROLL // Avec ascenseur WS_GROUP // Si la fenêtre est la première d'un groupe activable par Tabstop WS_TABSTOP // Activable par Tabstop Styles étendus (à utiliser avec CreateWindowEx) WS_EX_ACCEPTFILES // Specifies that a window created with this style accepts drag-drop files. WS_EX_APPWINDOW // Forces a top-level window onto the taskbar when the window is visible. WS_EX_CLIENTEDGE // Specifies that a window has a border with a sunken edge. WS_EX_CONTEXTHELP // Includes a question mark in the title bar of the window. When the user clicks the question mark, the cursor changes to a question mark with a pointer. If the user then clicks a child window, the child receives a WM_HELP message. The child window should pass the message to the parent window procedure, which should call the WinHelp function using the HELP_WM_HELP command. The Help application displays a pop-up window that typically contains help for the child window. WS_EX_CONTEXTHELP // cannot be used with the WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles. WS_EX_CONTROLPARENT // Allows the user to navigate among the child windows of the window by using the tab key. WS_EX_DLGMODALFRAME // Creates a window that has a double border; the window can, optionally, be created with a title bar by specifying the WS_CAPTION style in the dwStyle parameter. WS_EX_LEFT // Window has generic "left-aligned" properties. This is the default. WS_EX_LEFTSCROLLBAR // If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the vertical scroll bar (if present) is to the left of the client area. For other languages, the style is ignored and not treated as an error. WS_EX_LTRREADING // The window text is displayed using Left to Right reading-order properties. This is the default. WS_EX_MDICHILD // Creates an MDI child window. WS_EX_NOPARENTNOTIFY // Specifies that a child window created with this style does not send the WM_PARENTNOTIFY message to its parent window when it is created or destroyed. WS_EX_OVERLAPPEDWINDOW // Combines the WS_EX_CLIENTEDGE and WS_EX_WINDOWEDGE styles. WS_EX_PALETTEWINDOW // Combines the WS_EX_WINDOWEDGE, WS_EX_TOOLWINDOW, and WS_EX_TOPMOST styles. WS_EX_RIGHT // The window has generic "right-aligned" properties. This depends on the window class. This style has an effect only if the shell language is Hebrew, Arabic, or another language that supports reading order alignment; otherwise, the style is ignored and not treated as an error. WS_EX_RIGHTSCROLLBAR // Vertical scroll bar (if present) is to the right of the client area. This is the default. WS_EX_RTLREADING // If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the window text is displayed using Right to Left reading-order properties. For other languages, the style is ignored and not treated as an error. WS_EX_STATICEDGE // Creates a window with a three-dimensional border style intended to be used for items that do not accept user input. WS_EX_TOOLWINDOW // Creates a tool window; that is, a window intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the taskbar or in the dialog that appears when the user presses alt+tab. If a tool window has a system menu, its icon is not displayed on the title bar. However, you can display the system menu by right-clicking or by typing alt+space. WS_EX_TOPMOST // Specifies that a window created with this style should be placed above all non-topmost windows and should stay above them, even when the window is deactivated. To add or remove this style, use the SetWindowPos function. WS_EX_TRANSPARENT // Specifies that a window created with this style should not be painted until siblings beneath the window (that were created by the same thread) have been painted. The window appears transparent because the bits of underlying sibling windows have already been painted. To achieve transparency without these restrictions, use theSetWindowRgn function. WS_EX_WINDOWEDGE // Specifies that a window has a border with a raised edge. Using the WS_EX_RIGHT style for static or edit controls has the same effect as using the SS_RIGHT or ES_RIGHT style, respectively. Using this style with button controls has the same effect as using BS_RIGHT and BS_RIGHTBUTTON styles. Le style standard d'une fenêtre non-enfant est: WS_OVERLAPPEDWINDOW Le style standard d'une fenêtre enfant est: WS_CHILD | WS_VISIBLE Si le style WS_VISIBLE est utilisé, CreateWindow produit l'envoi de tous les messages (WM_CREATE, puis WM_SIZE puis WM_PAINT) nécessaires pour faire apparaitre la fenêtre. WM_CREATE est envoyé dans tous les cas. Rappel : donner le focus à la fenêtre hWnd : ------------------------------------------ SetFocus(hWnd); Maintenir une fenêtre au 1er plan : --------------------------------- Une fenêtre activée, peut remettre au 1er plan une autre fenêtre par : case WM_ACTIVATE : if (hWndTruc) BringWindowToTop(hWndTruc) ; return 0 ; Accès et/ou modifications des paramètres de classe d'une fenêtre: ---------------------------------------------------------------- On peut (si nécessaire) lire ou modifier les paramètres enregistrés par RegisterClassEx à l'aide de GetClassLong et SetClassLong. Exemple: SetClassLong(hWnd,index,valeur); où index vaut GCL_STYLE,GCL_WNDPROC, GCL_CBCLSEXTRA,GCL_CBWNDEXTRA,GCL_HICON, GCL_HCURSOR,GCL_HBRBACKGROUND,GCL_HMODULE ou GCL_MENUNAME suivant le paramètre que l'on désire modifier. Exemples: //Modification de la brosse de fond: Oldbrush = (HBRUSH) SetClassLong (hWnd, GCL_HBRBACKGROUND, (LONG) CreateSolidBrush (RGB (r,g,b))) ; DeleteObject(Oldbrush); //Modification de l'icone: SetClassLong(hWnd, GCL_HICON, LoadIcon(hinst,"newIcon")); Accès et/ou modifications des paramètres de création d'une fenêtre: ------------------------------------------------------------------ On peut (si nécessaire) lire ou modifier les paramètres enregistrés par CreateWindow à l'aide de GetWindowLong et SetWindowLong utilisés avec les paramètres suivants: GWL_EXSTYLE Accès au style étendu. GWL_STYLE Accès style création GWL_WNDPROC Nouvelle window procedure. GWL_HINSTANCE Nouveau hinst. GWL_ID Nouveau hmenu ou ID. Exemples: hinst = (Hinstance) GetWindowLong(hWnd, GWL_HINSTANCE); // accés à hinstance id = GetWindowLong(hWndChild, GWL_ID); // accés à l'id fenêtre enfant /* Remplacement de la procédure fenêtre standard par une autre plus spécifique */ StdProc = (WNDPROC) SetWindowLong(hWndchild, GWL_WNDPROC, (LONG) SpecProc); Le changement du texte se fait simplement par SetWindowText(hWnd,"texte"); Accès à hinstance: plusieurs solutions: ----------------- 1) Le mémoriser dans une variable globale 2) Le récupérer n'importe où par : hinst = (HINSTANCE) GetWindowLong(hWnd,GWL_HINSTANCE); 3) Le récupérer n'importe où par : hinst = GetModuleHandle (NULL); 4) dans le traitemement de WM_CREATE par: ((LPCREATESTRUCT) lParam)->hInstance car lParam contient l'adresse d'une CREATESTRUCT qui possède hinstance comme membre. Accès à hWnd: ------------ hWnd = GetFocus(); hWnd = GetForegroundWindow() ; Utilisation des octets à tout faire de fenêtre: ---------------------------------------------- SetWindowWord(hWnd,offset,val); permet de mettre le WORD val à l'offset (compté en octets) dans la zone réservée par wndclass.cbWndExtra. GetWindowWord(hWnd,offset); recupère le WORD placé au rang offset. Déplacement et/ou redimensionnement de fenêtres (parents, enfants, boutons, etc...): ----------------------------------------------------------------------------------- MoveWindow(hWnd,PosX,PosY,largeur,hauteur,flagRepaint); PosX et Posy sont en coordonnées écran pour une fenêtre parent et en coordonnées client de la fenêtre parent pour une fenêtre enfant. Accès au handle de la fenêtre parent (depuis une enfant): -------------------------------------------------------- hWndParent = GetParent(hWnd); Accès au handle d'une fenêtre enfant connaissant son Id: ------------------------------------------------------- hWndChild = GetDlgItem(hWndParent, id); Accès à l'id d'un enfant connaissant son handle: ----------------------------------------------- id = GetDlgCtrlID(hWndChild); // Le plus simple id = GetWindowLong(hWndChild, GWL_ID); // A peine plus compliqué. Changement du texte fenêtre (parent, enfant, ...): --------------------------- SetWindowText(hWnd,"Nouveau texte fenêtre"); Activation/desativation (grisée) d'une fenêtre : ---------------------------------------------- EnableWindow(hWnd, TRUE ou FALSE); Montrer/Cacher une fenêtre : -------------------------- ShowWindow(hWnd, TRUE ou FALSE); FENETRE ENFANT DE LA CLASSE PRE-ENREGISTREE "button": --------------------------------------------------- hWndButton = CreateWindow ("button", szTexte, WS_CHILD | WS_VISIBLE | style_bouton, PosX,PosY, largeur, hauteur, hWndParent, (HMENU)Id_du_bouton, hinstance, NULL) ; avec style_button = BS_PUSHBUTTON, BS_DEFPUSHBUTTON, BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE, BS_AUTO3STATE, BS_RADIOBUTTON, BS_AUTORADIO, BS_GROUPBOX ou BS_OWNERDRW. Message WM_COMMAND des fenêtre "button" au parent: -------------------------------------- Quand il y a un click sur un bouton, Windows envoie au parent le message WM_COMMAND avec : lParam = handle de la fenêtre enfant HIWORD(wParam) = 0 = BN_CLICKED) {ou 5 = BN_DOUBLECLICKED, RADIOBUTTON uniquement} LOWORD(wParam) = Id de la fenêtre enfant. Boutons poussoir de style = BS_PUSHBUTTON ou BS_DEFPUSHBUTTON: ------------------------------------------------------------- Utilisés pour déclencher une action. Dimensions idéales : largeur = cxChar*(strlen(szTexte)+2), hauteur = 7*cyChar/4. Si le bouton est cliqué Windows génère: SendMessage(hWndParent, WM_COMMAND, MAKELONG(Id,BN_CLICKED),(LPARAM) hWndChild); On peut simuler le clignotement d'un bouton poussoir en envoyant les messages suivants: SendMessage(hWndbutton,BM_SETSTATE,1,0); // Bouton enfoncé avec pointillés de sélection SendMessage(hWndbutton,BM_SETSTATE,0,0); // Bouton normal Bouton à cocher de style = BS_CHECKBOX, BS_AUTOCHECKBOX,BS_3STATE,BS_AUTO3STATE: ------------------------------------------------------------------------------- Utilisés pour marquer et démarquer un état et agir en conséquence. Le texte est à droite de la case sauf si le style BS_LEFTTEXT est spécifié. Les cases de BS_AUTOCHECKBOX et BS_AUTO3STATE sont automatiquement gérées et marquées. Pour lire l'état de ces boutons : etatbut = (int) SendMessage(hWndbutton, BM_GETCHECK,0,0); renvoie 0 pour vide, 1 pour marqué et 2 pour grisé (cas BS_3STATE). Pour les autres boutons, etatbut est l'état du bouton avant le clic. Pour cocher ou décocher la case de BS_CHECKBOX on fera : SendMessage(hWndbutton,BM_SETCHECK, 1 ou 0 ,0); Boutons BS_RADIOBUTTON et BS_AUTORADIO: -------------------------------------- Utilisés pour choisir une action parmi plusieurs. Normalement, une fois marqué, le bouton radio doit rester marqué, jusqu'à ce qu'un autre bouton radio du même groupe lui prenne la marque. Le reste du comportement est identique aux boutons précédents. On peut prémarquer le bouton numéro n lors du traitement du WM_INITDIALOG par CheckRadioButton(hDlg,IDC_RADIO1,IDC_RADIO4,IDC_RADIO1+n); (exemple avec 4 boutons radio). On récupère le numéro du bouton coché par : switch (message) { case WM_COMMAND : switch (LOWORD(wParam)) { case IDC_RADIO1: case IDC_RADIO2: case IDC_RADIO3: case IDC_RADIO4: n = LOWORD(wParam) - IDC_RADIO_FIRST; return TRUE; ................... Boutons BS_OWNERDRW (Voir OWNERDRW §8 page 429): ------------------- Complètement défini par l'utilisateur. Windows envoie le message WM_DRAWITEM chaque fois que le bouton doit être dessiné par l'usager, à sa création et suite à tout changement. Lors de la réception de WM_DRAWITEM, lParam contient un pointeur sur une structure LPDRAWITEMSTRUCT {pdis = (LPDRAWITEMSTRUCT) lParam;} ayant pour champs: hDC contexte de périf du bouton, rcItem structure RECT contenant la taille du bouton CtlID l'identificateur de la fenêtre de contrôle (déclarée lors de CreateWindow) itemState état du bouton. ODS_SELECT & pdis->itemState : 1 => le bouton est enfoncé/non enfoncé ODS_FOCUS & pdis->itemState : 1 => le bouton à le focus Si le bouton a le focus, on peut le signaler en dessinant le rectangle pointillé par: DrawFocusRect(pdis->hDC, pdis->rcItem); FENETRE ENFANT DE LA CLASSE PRE-ENREGISTREE "static": ---------------------------------------------------- Ne prennent aucune entrée et ne renvoie aucun message WM_COMMAND à la fenêtre parent. On peut les utiliser pour afficher un fond uni de couleur ou du texte. Rectangles unis pleins: SS_BLACKRECT, SS_GRAYRECT, SS_WHITERECT. Cadres : SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME. Ombres: SS_ETCHEDHORIZ, SS_ETCHEDVERT, SS_ETCHEDFRAME. Les couleurs de remplissage ou de cadre sont les couleurs systèmes suivantes: BLACK -> COLOR_3DDKSHADOW GRAY -> COLOR_BTNSHADOW WHITE -> COLOR_BTNHIGHLIGHT Avec ces styles (précédents) le texte de fenêtre n'est pas pris en compte. Sans ces styles, le texte de fenêtre peut être formaté par les styles SS_LEFT, SS_RIGHT ou SS_CENTER. Ce texte peut être changé par SetWindowText. FENETRE ENFANT DE LA CLASSE PRE_ENREGISTREE "scrollbar": ------------------------------------------------------- Exemple de création (Colors1 §8 page 440): CreateWindow ("scrollbar", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT, 0, 0, 0, 0, hWnd, (HMENU) id, hInstance, NULL) ; Le style SBS_VERT sert pour un controle générant des commandes d'ascenseur vertical et SBS_HORZ pour un contrôle horizontal. Les commandes générées sont les mêmes que celles générées par les barres de fenêtre crées à l'aide des styles WS_VSCROLL et WS_HSCROLL. Pour différencier, les commandes WM_VSCROLL et WM_HSCROLL provenant des barres de défilement de la fenêtre de celles des contrôles de défilement, celles de la fenêtre ont lParam = 0, alors que pour les "contrôles" de défilement lParam représente le hWnd de la fenêtre enfant. Dans le deux cas LOWORD(wParam) indique l'action de la souris sur la barre (SB_TOP, SB_BOTTOM, SB_LINEUP, SB_LINEDOWN, SB_PAGEUP, SB_PAGEDOWN, SB_THUMBTRACK, SB_THUMBPOSITION). SetScrollRange(hWndscr, SB_CTL, xmin, xmax, bRedraw); // Fixe la dynamique SetScrollPos(hWndscr, SB_CTL, ipos, bRedraw) ; // Fixe la position de l'ascenseur _CTL, &sructscr, bRedraw) ; // Fixe tout Le style WS_TABSTOP permet la capture du focus par la fenêtre scrollbar quand on clique à l'intérieur. Si on désire communiquer le focus à une autre fenêtre en appuyant sur la touche TAB, il faut en outre traiter WM_KEYDOWN par la procédure fenêtre enfant, ce que ne fait pas Windows. Pour cela on ajoute un traitement spécifique au traitement standard de la fenêtre enfant (Voir sous-classement de fenêtre). FENETRE ENFANT DE LA CLASSE PRE-ENREGISTREE "edit": -------------------------------------------------- hWndEdit = CreateWindow("edit", NULL, // Pas de titre par exemple WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, PosX,PosY, largeur, hauteur, hWndParent, (HMENU)Id_de_l_editwindow, hinstance, NULL); Pour éditer du texte. Offre en standard le COPY, CUT, PASTE. Les styles les + couramment utilisés sont (en sus de WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL): WS_BORDER pour avoir un cadre autour de la fenêtre (très fin). ES_LEFT pour justifier le texte à gauche ES_MULTILINE pour permettre l'édition multilignes ES_AUTOHSCROLL pour générer un décalage à droite (ou à gauche) automatique à la place d'un saut de ligne quand le curseur atteint le bord latéral de la fenêtre (redondant avec WS_HSCROLL qui offre en plus la barre de défilement horizontale) ES_AUTOVSCROLL pour générer un décalage vertical automatique quand le curseur atteint le bord haut ou bas de la fenêtre (redondant avec WS_VSCROLL qui offre en plus la barre de défilement verticale). Styles moins courants: ES_CENTER Centre le texte dans un contrôle d'édition multilignes. ES_RIGHT Justifie le texte à droite. ES_LOWERCASE Convertit tous les caractères en minuscules ES_UPPERCASE Convertit tous les caractères en majuscules ES_NOHIDESEL Maintient la partie sélectionnée en surbrillance quand perd le focus. ES_OEMCONVERT Conversion du texte entrée d'ANSI en OEM. ES_PASSWORD Remplace le texte tapé par des *. ES_READONLY Empêche la modification du texte. ES_WANTRETURN Interprêtre le CR comme un CR et non comme appui sur la case OK (cas d'une boîte de dialogue). Messages WM_COMMAND envoyés par le contrôle d'édition à la fenêtre parent: ------------------------------------------------------------------------- Comme pour toutes les fenêtres enfants, lParam contient le handle de la fenêtre enfant, LOWORD(wParam) contient le numéro ID de la fenêtre enfant et HIWORD(wParam) contient un des codes de notification suivants: HIWORD(wParam): EN_SETFOCUS Le contrôle d'édition a obtenu le focus d'entrée. EN_KILLFOCUS Le contrôle d'édition a perdu le focus d'entrée. EN_CHANGE Le contenu du contrôle d'édition va être modifié. EN_UPDATE Le contenu du contrôle d'édition a été modifié. EN_ERRSPACE Le contrôle d'édition est à cours d'espace. EN_MAXTEXT Le contrôle d'édition est à cours d'espace en insertion. EN_HSCROLL La barre de défilement verticale a été cliquée. EN_VSCROLL La barre de défilement horizontale a été cliquée. Accès au texte du contrôle d'édition: ------------------------------------ On peut avoir une copie du texte du contrôle d'édition dans un szBuffer par: (voir également le message EM_GETLINE ci-après). nbCharLus = GetWindowText(hWnd, szBuffer, nbMaxchar) ; ou encore (cas d'un contrôle d'édition de boite de dialogue) : GetDlgItemText(hDlg, IDC_EDIT, szBuffer, nbMaxchar) On peut mettre un nouveau texte dans le contrôle d'édition avec : SetWindowText(hWnd, szBuffer); où encore (cas d'un contrôle d'édition de boite de dialogue) : SetDlgItemText(hDlg, IDC_EDIT, szBuffer); Les messages à un contrôle d'édition: ------------------------------------ Pour provoquer l'échange de texte avec le presse-papier on peut envoyer les messages suivants au contrôle d'édition: SendMessage(hWndEdit, WM_CUT, 0, 0); // Sélection coupée vers presse-papier SendMessage(hWndEdit, WM_COPY, 0, 0); // Sélection copiée vers presse-papier SendMessage(hWndEdit, WM_CLEAR, 0, 0); // Sélection effacée SendMessage(hWndEdit, WM_PASTE, 0, 0); // Presse-papier inséré Accés à la position de la zone sélectionnée, ou du curseur: ---------------------------------------------------------- SendMessage(hWndEdit, EM_GETSEL, (WPARAM)&deb, (LPARAM)&fin); fin est l'index du dernier caractère sélectionné +1. Le nb de caractères sélectionnés =fin-deb. Si rien n'est sélectionné deb=fin=position du prochain caractère à insérer. On peut forcer la sélection d'une zone: --------------------------------------- SendMessage(hWndEdit, EM_SETSEL, deb, fin); On peut remplacer le texte sélectionné (ou insérer du texte): ------------------------------------------------------------- SendMessage(hWndEdit, EM_REPLACESEL, (WPARAM)fCanUndo, (LPARAM) szString); // fCanUndo (0/1) si Undo autorisé Nb total de lignes: ------------------ iCount = SendMessage(hWndEdit, EM_GETLINECOUNT, 0, 0); Rang du 1er caractère de la ligne iLine (0 a iCount-1) ou de la ligne contenant le curseur (si iLine -1): iOffset = SendMessage(hWndEdit, EM_LINEINDEX, iLine, 0); Nombre de caractères de la ligne contenant le caractère de rang iOffset (ou Nb de caractères non sélectionnés dans la ou les lignes contenant la sélection si iOffset -1): iLength = SendMessage(hWndEdit, EM_LINELENGTH, iOffset, 0); Numéro iLine (0 à ...) de la ligne contenant le caractère de rang iOffset: iLine = SendMessage(hWndEdit, EM_LINEFROMCHAR, iOffset, 0); Lecture de la ligne numéro iLine (0 a iCount-1): *((WORD *) Buffer)= Dimbuf-1 ; // 0n indique le nb max de char dans le 1er WORD. iLength = SendMessage(hWndEdit, EM_GETLINE, iLine, (LPARAM) Buffer); // Lecture Buffer[iLength] = (char) 0; // 0n ajoute le zéro à la fin du string lu. FENETRE ENFANT DE LA CLASSE PRE-ENREGISTREE "listbox" ----------------------------------------------------- Elle permet d'afficher des listes de strings (un par ligne) triées (ou non) par ordre alphabétique, d'en enlever, ajouter, sélectionner, ... Elle est généralement crée avec les styles WS_CHILD, WS_VISIBLE et LBS_STANDART = (LBS_NOTIFY | LBS_SORT | WS_SCROLL | WS_BORDER). Pour sa dimension verticale on pourra prendre (int)(tm.tmHeight * X.99), ou X est le nb de lignes désirées (la dimension est réajustée par windows). LBS_SORT demande un classement alphabétique des strings. LBS_NOTIFY autorise le contrôle listbox à envoyer des messages WM_COMMAND à sa fenêtre parent, avec comme d'habitude lParam = hWnd parent, LOWORD(wParam) = ID de l'enfant et HIWORD(wParam): LBN_ERRSPACE Saturation de la zone de liste LBN_SELCHANGE Modification de la sélection LBN_DBLCLK Article a été double-cliqué LBN_SETFOCUS La listbox reçoit focus (peu intéressant) LBN_KILLFOCUS La listbox perd le focus (peu intéressant) LBS_MULTIPLESEL permet d'effectuer des sélectioins multiples. WS_SIZEBOX permet à l'utilisateur de redimensionner la liste. WS_CAPTION permet à l'utilisateur de déplacer la liste. A chaque modifification, l'affichage de la liste est automatiquement mis-à-jour, sauf si le style LBS_NOREDRAW a été utilisé. On peut contrôler cette mise-à-jour par: SendMessage(hWndlist, WM_SETREDRAW, bRedraw, 0); bRedraw = FALSE interdit la mise à jour (pendant une série de modifs par exemple) et bRedraw = TRUE la réactive. Manipulation des listes: ----------------------- Après la création, on commencera par mettre des strings dans la liste, pour pouvoir ensuite les manipuler. Tout se fait par des messages. Ajout d'un string dans la liste (à sa place si ordonnée): zOK = SendMessage(hWndlist, LB_ADDSTRING, 0, (LPARAM) szString); Insertion d'un string dans la liste, avant le rang iRang (-1 => en queue): zOK = SendMessage(hWndlist, LB_INSERTSTRING, iRang, (LPARAM) szString); Nombre d'éléments: iCount = SendMessage(hWndlist, LB_GETCOUNT, 0, 0); Suppression du string de rang iRang (0, ...) de la liste: SendMessage(hWndlist, LB_DELETESTRING, iRang, 0); Effacement de toute la liste: SendMessage(hWndlist, LB_RESETCONTENT, 0, 0); Sélection (initiale par défaut par ex.) de l'élément iRang (-1 => tous) de la liste: SendMessage(hWndlist, LB_SETCURSEL, iRang, 0); //liste simple SendMessage(hWndlist, LB_SETSEL, bSel, iRang);//liste multiple (bSel=!0/0->Sel/Desel) Rang ou état de la selection courante (-1 si aucun): iRang = SendMessage(hWndlist, LB_GETCURSEL, 0, 0); //simple bSel = SendMessage(hWndlist, LB_GETSEL, iRang, 0); //multiple (bSel=!0/0->Sel/Desel) Recherche d'un string par ses premiers caractères à partir de iRang(-1 => en haut): iRang = SendMessage(hWndlist, LB_SELECTSTRING, iRang, (LPARAM) szString); Longueur du string iRang: iLength = SendMessage(hWndlist, LB_GETTEXTLEN, iRang, 0); Copie du string iRang dans szBuffer: iLength = SendMessage(hWndlist, LB_GETTEXT, iRang, (LPARAM) szBuffer); LISTE DE FICHIERS: ----------------- On remplit une listbox avec la liste des fichiers du répertoire courant par: SendMessage(hWndlist, LB_DIR, iAttr, (LPARAM) szFileSpec); où szFileSpec est par exemple "*.*" et où iAttr est le filtre de sélection des fichiers par les attributs: 0x0000 DDL_READWRITE Inclure fichiers read-write (par défaut). 0x0001 DDL_READONLY Inclure fichiers read-only. 0x0002 DDL_HIDDEN Inclure fichiers cachés. 0x0004 DDL_SYSTEM Inclure fichiers systèmes. 0x0010 DDL_DIRECTORY Inclure sous-répertoires (sous forme [SOUSREP]). 0x0020 DDL_ARCHIVE Inclure fichiers archives. 0x4000 DDL_DRIVES Inclure les lecteurs (sous forme [-X-]). 0x8000 DDL_EXCLUSIVE Exclure les fichiers read-write si non spécifiés. DDL_READWRITE est automatique par défaut, sauf si DDL_EXCLUSIVE est positionné. Exemple d'ouverture d'un fichier au moyen d'une listbox (cf Head.c page 471) static OFSTRUCT ofs ; case WM_COMMAND : if (LOWORD (wParam) == 1 && HIWORD (wParam) == LBN_DBLCLK) { // accès rang ligne sélectionnée if (LB_ERR == (i = SendMessage (hWndlist,LB_GETCURSEL, 0, 0L))) break ; // accès texte sélectionné SendMessage (hWndlist, LB_GETTEXT, i, (LPARAM) szBuffer) ; if (-1 != (iHandle = OpenFile (szBuffer, &ofs, OF_READ))) { // Fichier valide nbcRead = _lread (iHandle, sReadBuffer, MAXREAD) ; _lclose (iHandle) ; // Fabrique chemin+nomfich pour le fun strcpy (szFile, szBuffer) ; // Mémo nomfich getcwd (szBuffer, MAXPATH) ; if (szBuffer [strlen (szBuffer) - 1] != '\\') strcat (szBuffer, "\\") ; strcat (szBuffer, szFile) ; } else { // Réception "[nomrep]", on va changer de répertoire szBuffer [strlen (szBuffer) - 1] = '\0' ; // devient "[nomrep" chdir (szBuffer + 1) ; // chdir vers "nomrep" getcwd (szBuffer, MAXPATH) ; // pour avoir chemin+nomrep SendMessage (hWndlist, LB_RESETCONTENT, 0, 0L) ; //Efface liste SendMessage (hWndlist, LB_DIR, 0x37, (LONG) "*.*") ; //Nouvelle liste } } FENETRE ENFANT DE LA CLASSE PRE-ENREGISTREE "Combobox" ----------------------------------------------------- Elle a les fonctionnalités d'une listbox (manipulation d'une liste d'éléments) et d'un contrôle d'édition. Exemples d'intructions : SendMessage(HWNDCB, CB_RESETCONTENT, 0, 0); // Raz du contenu di combo SendMessage(HWNDCB, CB_ADDSTRING, 0, (LPARAM) sztexte); //ajout d'un item ds la liste Acces au texte du combo (une des 3 méthodes au choix) : SendDlgItemMessage(hDlg, IDC_COMBO, WM_GETTEXT, sizeof(sztxt),(LPARAM) sztxt); GetDlgItemText(hDlg, IDC_COMBO1, sztxt, sizeof(sztxt)); GetWindowText(GetDlgItem(hDlg, IDC_COMBO1), sztxt, sizeof(sztxt)); SOUS-CLASSEMENT DE FENETRE ENFANT: --------------------------------- C'est la méthode qui permet d'intercaler une procédure de fenêtre enfant spécifique avant l'appel de la procédure standard. Cela permet de faire un traitement spécifique non prévu en standard. Pour cela il faut substituer à l'adresse inconnue de la procédure standard 'StdProc', l'adresse de la procédure spécifique 'SpecProc' : // Substitution et mémo @StdProc : StdProc = (WNDPROC) SetWindowLong(hWndchild, GWL_WNDPROC, (LONG) SpecProc); et appeler StdProc dans le return de SpecProc : LRESULT CALLBACK SpecProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { int i = GetWindowLong (hWnd, GWL_ID) ; ......... switch (iMsg) { .... } return CallWindowProc (StdProc, hWnd, iMsg, wParam, lParam) ; } Exemple 1: Contrôle de liste qui simule la réception d'un double-clic quand il reçoit un RETURN: LRESULT CALLBACK SpecProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { int i = GetWindowLong (hWnd, GWL_ID) ; if (iMsg == WM_KEYDOWN && wParam == VK_RETURN) SendMessage (GetParent (hWnd), WM_COMMAND, MAKELONG(i,LBN_DBLCLK),(LPARAM)hWnd); return CallWindowProc (StdProc, hWnd, iMsg, wParam, lParam) ; } Exemple 2: Circulation du focus avec la touche TAB entre Nbe fenêtres enfants: LRESULT CALLBACK SpecProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { int i = GetWindowLong (hWnd, GWL_ID) ; switch (iMsg) { case WM_KEYDOWN : if (wParam == VK_TAB) SetFocus (hWndScrol[(i+(GetKeyState(VK_SHIFT)<0 ? Nbe-1 : 1)) % Nbe]); break ; case WM_SETFOCUS : iFocus = i ; //La fenetre parent fait SetFocus(iFocus) qd elle le reçoit. break ; } return CallWindowProc (StdProc[i], hWnd, iMsg, wParam, lParam) ; } MODIFICATION DES COULEURS DES FENETRES ENFANTS: ---------------------------------------------- Elle peut se faire en traitant les messages WM_CTLCOLORXXX... Quand on traite ce message, la procédure a dejà obtenu un HCD de la fenêtre enfant. Il est accessible par wParam. lParam est le handle de la fenêtre enfant qui envoit le message WM_CTL.. à sa fenêtre parent. Le résultat renvoyé par le return du traitement du message est la brosse qui sera utilisée pour remplir le fond de la fenêtre enfant. Exemple: case WM_CTLCOLORXXX..: i = GetWindowLong((hWnd) lParam, GWL_ID); // Accès à l'ID de la fenêtre enfant if (i == ...) // Est-ce la fenêtre enfant concernée ? { SetTextColor((HDC) wParam, RGB(r,g,b)); // Couleur texte fenêtre enfant SetBkColor ((HDC) wParam, RGB(r,g,b)); // Couleur fond de texte " " return GetStockObject(NULL_BRUSH); } break ; On ne peut pas modifier les couleurs des "button" BS_PUSHBUTTON et BS_DEFPUSHBUTTON. WM_CTLCOLORBTN permet de modifier la couleur "button" BS_OWNERDRW. WM_CTLCOLORSTATIC permet de modifier la couleur des "button" BS_CHECKBOX, BS_AUTOCHECKBOX, BS_RADIOBUTTON, BS_3STATE, BS_AUTO3STATE, BS_GROUPBOX et BS_AUTORADIOBUTTON. Dans le cas de BS_GROUPBOX, seul le texte de fenêtre est affecté. WM_CTLCOLORSTATIC permet de modifier la couleur des fenêtres enfants "static". WM_CTLCOLORSCROLLBAR permet de modifier la couleur des fenêtres enfants "scrollbar". COULEURS SYSTEMES PREDEFINIES: ----------------------------- S'obtiennent une par une avec GetSysColor et se modifient par tableaux avec SetSysColors. Exemple: SetTextColor(hdc,GetSysColor(COLOR_WINDOWTEWT)); COLOR_ACTIVEBORDER Active window border. COLOR_ACTIVECAPTION Active window caption. COLOR_APPWORKSPACE Background color of multiple document interface (MDI) applications. COLOR_BACKGROUND Desktop. COLOR_BTNFACE (WNT) Surface ou fond des boutons. COLOR_BTNHILIGHT (W95) Highlight color for buttons (same as COLOR_3DHILIGHT). COLOR_BTNSHADOW Ombre des boutons. COLOR_BTNTEXT Text on push buttons. COLOR_CAPTIONTEXT Text in caption, size box, and scroll bar arrow box. COLOR_GRAYTEXT Grayed (disabled) text. This color is set to 0 if the current display driver does not support a solid gray color. COLOR_HIGHLIGHT Item(s) selected in a control. COLOR_HIGHLIGHTTEXT Text of item(s) selected in a control. COLOR_INACTIVEBORDER Inactive window border. COLOR_INACTIVECAPTION Inactive window caption. COLOR_INACTIVECAPTIONTEXT Color of text in an inactive caption. COLOR_INFOBK (W95) Background color for tooltip controls. COLOR_INFOTEXT (W95) Text color for tooltip controls. COLOR_MENU Menu background. COLOR_MENUTEXT Windows NT only: Text in menus. COLOR_SCROLLBAR Scroll bar gray area. COLOR_WINDOW Window background. COLOR_WINDOWFRAME Lignes de séparation des fenêtres. COLOR_WINDOWTEXT Text in windows. COLOR_3DDKSHADOW (W95) Dark shadow for three-dimensional display elements. COLOR_3DFACE (W95) Face color for three-dimensional display elements. COLOR_3DHILIGHT (W95) Highlight color for three-dimensional display elements (for edges facing the light source.) COLOR_3DLIGHT (W95) Light color for three-dimensional display elements (for edges facing the light source.) COLOR_3DSHADOW (W95) Shadow color for three-dimensional display elements (for edges facing away from the light source). ----------------------------------------------------------------- UTILISATION DE RESSOURCE (ICONES,CURSEURS,BITMAP,FICHIERS DIVERS) ----------------------------------------------------------------- On appelle ressources des éléments affichés par les programmes qui peuvent être décrits dans des fichiers, tels que des icônes, des curseurs, des bitmaps, des chaînes de caractères, etc,.... Le fait de les définir en tant que ressource: - fait que certaines sont partagées par les différentes instances du programme, - permet de les modifier sans qu'il soit nécessaire de recompiler le programme, - laisse à windows le soin de gérer leur présence en mémoire Ram ou Disque. Pour utiliser ces ressources, il faut les déclarer dans un fichier .rc appelé "script de ressource". Chaque ressource est déclarée dans une ligne avec 3 éléments: - l'identificateur de la ressource (myicon) - le type de la ressource prédéfini (ICON,..) ou nouveau, - le nom du fichier décrivant la ressource (fichicon.ico) Le fichier .rc est compilé pour générer un fichier binire d'extension .res par la commande DOS: RC -r -DWIN32 fichres.rc -r -> génération de fichres.res -DWIN32 -> #define WIN32 Exemple de fichres.rc: /* Script de ressources */ myicon ICON fichico1.ico 125 ICON fichico2.ico mycurs CURSOR fichcur1.cur 126 CURSOR fichcur2.cur mybmap BITMAP fichmap1.bmp 127 BITMAP fichmap2.bmp STRINGTABLE { 128 "Premier message" 129 "Deuxième message" } mytext PERSO fichtxt1.asc Les fichiers icônes ont l'extension .ico, les fichiers curseurs .cur et les bitmaps .bmp. L'identificateur de la ressource est soit un nom (case des caractères ignorée), soit un nombre d'identification 0 < ID < 65536 (accès plus efficace). Dans le programme, il faut d'abord charger la ressource par un loadXxxx(hinst,???) où Xxxx dépend de la ressource (Icon, Cursor,..) et où ??? est le nom de la ressource sous forme d'un LPCTSTR. Exemples: hIcon = LoadIcon(hinst, IDI_APPLICATION); // IDI_APPLICATION est un LPCTSTR prédéf hIcon = LoadIcon(hinst, "myicon"); hIcon = LoadIcon(hinst, MAKEINTRESOURCE(125)); //caste 125 en LPCTSTR hIcon = LoadIcon(hinst, "#125"); // Autre possibilité hCursor = LoadCursor(hinst,MAKEINTRESOURCE(126)); hBitmap = LoadBitmap(hinst,MAKEINTRESOURCE(127)); LoadString(hinst,129,szBuf,sizbuf); // ici on met directement le numéro d'ID Exemples d'utilisation: DrawIcon(hdc,x,y,hIcon); SetCurcor(hCursor); hBrush = CreatePatternBrush(hBitmap); Atention bitmaps et brosses sont des objets du GDI. Il faut les effacer à la fin. DeleteObject(hBrush); DeleteObject(hBitmap); Cas particulier des ressources personnalisées: hRes = LoadResource(hinst,FindResource(hinst,MAKEINTRESOURCE(130),"PERSO"))); pText = (char *) LockResource(hRes); //Véritable accès. .... FreeResource(hRes); //Facultatif car automatique en fin de prog. On peut mettre un MAKEINTRESOURCE(id) à la place de "PERSO" avec 255 < id <65536 Il y a intérêt à mettre à la place des numéro d'ID, des identificateurs définis par des #define dans un .h qui sera mis en #include dans les fichiers .rc et .c ---------------------------- MENUS ET RACCOURCIS CLAVIERS ---------------------------- Définition menu dans le programme: ---------------------------------------------------- Un menu est directement créé dans un programme par les instructions suivantes : /*--------------------------------------------------------------------------*\ Création d'un menu. Renvoi le handle sur le menu créé ou NULL si échec Si réussite le handle sur les accélérateurs est également initialisé. \*--------------------------------------------------------------------------*/ HMENU MakeMenu(HACCEL *phaccel) /* { HMENU hbid; HMENU hMenu = CreateMenu() ; /* Menu Fichier : Nouveau, Ouvrir, Enreg., Enreg. sous, ---, Quitter */ if (!(hbid = CreateMenu())) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_NEW, "&Nouveau"))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_OPEN, "&Ouvrir..."))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_SAVE, "&Enregistrer"))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_SAVEAS, "En®istrer sous..."))) return NULL; if (!(AppendMenu(hbid, MF_SEPARATOR, 0, NULL))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_EXIT, "&Quitter"))) return NULL; if (!(AppendMenu(hMenu, MF_POPUP, (UINT) hbid, "&Fichier"))) return NULL; DestroyMenu(hbid); /* Menu Fond : Blanc, Gris, Noir */ if (!(hbid = CreateMenu())) return NULL; if (!(AppendMenu(hbid, MF_STRING , MENU_WHITE, "&Blanc"))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_GRAY, "&Gris"))) return NULL; if (!(AppendMenu(hbid, MF_STRING, MENU_BLACK, "&Noir"))) return NULL; if (!(AppendMenu(hMenu, MF_POPUP, (UINT) hbid, "F&ond"))) return NULL; DestroyMenu(hbid); /* Menu A propos */ if (!(AppendMenu(hMenu, MF_STRING, MENU_ABOUT, "&A propos..."))) return NULL; *phaccel = LoadAccelerators(GetModuleHandle (NULL), MAKEINTRESOURCE(hMenu)); return hMenu; } Ce menu est créé et affecté à la fenêtre hWnd par : if (!(hMenu = MakeMenu()) || !SetMenu(hWnd,hMenu)) MessageBox(hWnd,"Echec création Menu","",MB_OK); Définition d'un menu dans le fichier ressource: ----------------------------------------------- Les menus peuvent être décrits dans le fichier ressource fich.rc. Dans ce cas, un menu est déclaré par: - un identificateur de menu (nom ou ID qui sera convertit par MAKEINTRESOURCE), - le type MENU - une liste d'éléments de menu compris comprise entre { et } ou entre BEGIN et END. Exemple: Mymenu MENU { Liste d'éléments de menus } Un élément de menu est soit un MENUITEM terminal, soit un sous-menu. La déclaration d'un MENUITEM est de la forme: MENUITEM "Sa&lut", idm_item [,options] et celle d'un sous-menu est de la forme: POPUP "Sa&lut" [,options] { Liste d'éléments de menus } La ligne spéciale MENUITEM SEPARATOR produit une ligne de séparation dans un sous-menu. Dans la cellule, le texte écrit sera Salut. L'& facultatif produit le soulignement de la lettre suivante (le l). Si ce & est présent, l'appui de la touche ALT simultanément avec celui de lettre souligné produit le même effet qu'un clic sur la cellule. On peut modifier les MENUITEM : GRAYED L'élément de menu est initialement en gris et inactif. INACTIVE L'élément de menu est initialement inactif. MENUBREAK Les éléments suivant du menu ppal seront sur une nouvelle ligne. HELP Options supplémentaires pour les sous-menus: CHECKED Une marque initiale apparait à gauche du texte. MENUBARBREAK Les éléments suivant du sous-menu seront sur une nouvelle colonne. Exemple: #include "menudemo.h" MenuDemo MENU { POPUP "&Fichier" { MENUITEM "&Nouveau", IDM_NEW MENUITEM "&Ouvrir...", IDM_OPEN MENUITEM "&Enregistrer", IDM_SAVE MENUITEM "En®istrer sous...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "&Quitter", IDM_EXIT } POPUP "F&ond" { MENUITEM "&Blanc", IDM_WHITE, CHECKED MENUITEM "&Gris", IDM_GRAY MENUITEM "&Noir", IDM_BLACK } MENUITEM "&A propos...", IDM_ABOUT } Windows utilise le système suivant pour définir la zposition des éléments du menu. Dans le menu principal: 0 sous-menu "&Fichier" 1 sous-menu "F&ond" 2 item "&A propos..." et par exemple dans le sous-menu "F&ond": 0 item "&Blanc" 1 item "&Gris" 2 item "&Noir" Définition d'un memu popup -------------------------- Un menu popup bien conçu ne doit posséder qu'un item de type POPUP à son niveau le plus haut. Si on veut transformer le menu précédent en POPUP, il sufit de rajouter un item sous-menu (POPUP) de niveau plus élévé, avec un nom vide "", et de mettre tout le reste sous ce sous-menu. Exemple: #include "menudemo.h" MyMenu MENU { POPUP "" { POPUP "&Fichier" { MENUITEM "&Nouveau", IDM_NEW MENUITEM "&Ouvrir...", IDM_OPEN MENUITEM "&Enregistrer", IDM_SAVE MENUITEM "En®istrer sous...", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "&Quitter", IDM_EXIT } POPUP "F&ond" { MENUITEM "&Blanc", IDM_WHITE, CHECKED MENUITEM "&Gris", IDM_GRAY MENUITEM "&Noir", IDM_BLACK } MENUITEM "&A propos...", IDM_ABOUT } } Les actions sur le menu sont gérées au traitement du WM_COMMAND : --------------------------------------------------------------- LOWORD(wParam) -> idm_item; 0 = HIWORD(wParam) -> 0; lParam -> 0; Si menu de la fenêtre mère, sinon c'est le hWndchild Si l'action est traitée on renvoie 0. Exemple de délégation du traitement à une routine MenuHandler : if (!lParam && !HIWORD(wParam)) if (MenuHandler(hWnd, LOWORD(wParam))) return 0; Exemple de routine de traitement (renvoi 1 si traité et 0 sinon) : BOOL MenuHandler(HWND hWnd, int nMenuID) { //hmenu = GetMenu(hWnd); switch(nMenuID) { case MENU_ABOUT: // On affiche la boîte de dialogue MessageBox(hWnd,"A propos de quoi ??","",MB_OK); return 1; case MENU_EXIT: // On poste le message de demande de fin PostMessage(hWnd,WM_CLOSE,0,0L); return 1; case MENU_OPEN: MessageBox(hWnd,"MENU_OPEN Non implémenté","",MB_OK); return 1; } return 0; } Accès aux handles de menu et sous-menu: -------------------------------------- Le hMenu du menu déclaré dans le fichier ressource est obtenu par: hMenu = LoadMenu(hWnd,"MyMenu"); // etc... Des handles sur les sous-menus sont obtenus par leur zposition: hPopup1 = GetSubMenu(hMenu, 0); // Accès au sous-menu POPUP "" hPopup2 = GetSubMenu(hPopup1,1); // Accès au sous-menu POPUP"F&ond" Un handle sur le menu standard de la fenêtre peut être obtenu par: hMenu = GetMenu(hWnd); Un handle sur le menu système peut être obtenu par: hMenu = GetSystemMenu(hWnd,bRevert); Si bRevert = FALSE on s'autorise à modifier le menu (par des AppendMenu ..), si c'est TRUE, on restaure le menu système original. Affectation du menu standard à la fenêtre: -------------------------------------------- Diverse possiblités. Toutes ces ligne produisent l'affectation d'un menu standard qui sera situé en haut à gauche de la fenêtre.: wndclass.lpszMenuName = "MyMenu"; // ou MAKEINTRESOURCE(45) ou "#45" SetClassLong(hWnd, GCL_MENUNAME, hMenu)); // Modif au niveau de la classe hWnd = CreateWindow (-,-,-,-,-,-,-,-,hMenu,-,-); // Lors de la creation fen^tre SetMenu(hWnd,hMenu); // Remplacement n'importe quand ? Affectation d'un menu POPUP à la fenêtre: ---------------------------------------- En général on fait apparaître ce menu lors de la réception de WM_LBUTTONDOWN: case WM_LBUTTONDOWN: pos.x = LOWORD(lParam); pos.y = HIWORD(lParam) ; ClientToScreen(hWnd, &pos); TrackPopupMenu(hPopup,0,pos.x,pos.y,hWnd,NULL); return 0; A la place du paramètre 0 on peut spécifier des flags de positionnement et à la place du NULL, un rectangle à l'intérieut duquel on peut cliquer sans faire disparaitre le menu. Modification des menus: ----------------------- Les fonctions suivantes permettent de modifier la structure d'un menu: RemoveMenu(hMenu,uPos,uFlpos); // Supprime un item DeleteMenu(hMenu,uPos,uFlpos); // Supprime un item ou un popup InsertMenu(hMenu,uPos,uFlpos,uIDNewItem,lpNewItem); ModifyMenu(hMenu,uPos,uFlpos,uIDNewItem,lpNewItem); Les uPos sont soit des IDM_item si uFlpos vaut 0 ou MF_BYCOMMAND, ou la zposition s'il vaut MF_BYPOSITION. DrawMenuBar(hWnd); // Pour forcer le redessin de la barre des menus après une modif Mettre ou enlever la coche à un item du menu: CheckMenuItem(hMenu,uPos, uFlpos | MF_CHECKED); // MF_UNCHECKED pour l'oter la coche Activer ou desactiver un item du menu: EnableMenuItem(hMenu,uPos, uFlpos | MF_ENABLED); // MF_GRAYED pour désactiver Acces à l'état d'un item, Le resulat iflags sera une combinaison de MF_DISABLED, MF_GRAYED,MF_CHECKED, MF_MENUBREAK, MF_MENUBARBREAK, MF_SEPARATOR. iFlags = GetMenuState(hMenu,uPos,uFlPos); Destruction: DestroyMenu(hMenu); // S'il ne sert plus à rien BOITES DE DIALOGUE : ------------------ Soit modale (créée par DialoBox : on ne peut rien faire dans les autres fenêtres de l'appli tant que cette fenêtre est active), soit non modale (créée par CreateDialog : on peut travailler dans la mère, alors que cette fenêtre est tjs active). Si elle est système-modale (on ne peut rien faire d'autre avant de la quitter). Si la boîte de dialogue peut être décrite dans le fichier ressources ou dans un fichier à part (.dlg par convention). On l'inclut alors dans le fichier ressources par : rcinclude fichier.dlg Exemple : AproposBox DIALOG 20, 20, 160, 80 STYLE WS_POPUP | WS_DLGFRAME { CTEXT "About1" -1, 0, 12, 160, 8 ICON "About1" -1, 8, 8, 0, 0 CTEXT "Démonstration de boîte A propos" -1, 0, 36, 160, 8 CTEXT "(c) Charles Petzold, 1996" -1, 0, 48, 160, 8 DEFPUSHBUTTON "OK" IDOK, 64, 60, 32, 14, WS_GROUP } Ici, la première ligne donne le nom de la boîte de dialogue (AproposBox) suivi du mot clé DIALOG et des données left, top, largeur, hauteur (en 1/4 et 1/8 de caractères système) La deuxième précise le STYLE de la boîte : WS_POPUP : habituel WS_DLGFRAME : Mini encadrement standard WS_CAPTION : avec barre de titre et bordure. on precise le titre par une ligne juste en-dessous, ex : CAPTION "Titre de la boîte de dialogue" (ou par un SetWindowsText(...)) WS_SYSMENU : (suppose WS_CAPTION) ajoute un menu système. .... etc. On peut aussi ajouter un menu avec : MENU mon-menu Ensuite sont définis à l'intérieur de la boîte, 3 petites fenêtres de text centré (CTEXT), une icone (ICON) et un bouton Ok (DEFPUSHBUTTON), suivi de paramètres. Associée à la boîte de dialogue, la "procédure de boîte de dialogue" qui gère les messages qui lui sont destinés est appelée par la procédure de sa fenêtre qui elle est gérée par directement par Windows (on peut la gérer directement en spécifiant une ligne : CLASS "nom-classe"). A la différence d'une procédure de fenêtre, une procédure de boîte de dialogue ne retourne pas un LRESULT, mais un BOOL qui vaut TRUE quand elle a traité le message et FALSE quand elle ne l'a pas traité. Elle ne reçoit pas de WM_PAINT, WM_DESTROY, ... A la place de WM_CREATE elle reçoit WM_INITDIALOG. A la reception d'un WM_COMMAND on examine LOWORD(wParam) pour savoir quel contrôle a été sélectionné. Après traitement du contrôle, la procédure appelle normalement : EndDialog(hDlg, 0) ; return TRUE ; pour que Windows détruise la boîte. POUR CREER UNE BOITE DE DIALOGUE AVEC VC++ ------------------------------------------ Faire menu : Insert/Resource/Dialog. Une fenêtre d'edition de la fenêtre de dialogue s'ouvre dans laquelle on arrange les controles désirés et on édite leur propriétés en donnant des noms aux différents controles. Puis on sauve le fichier crée en *.rc, ce qui entraine automatiquement la création d'un fichier resource.h. Ensuite, il faut ajouter le fichier *.rc au projet : Menu Project/Add To Project/Files ... et ajouter #include "resource.h" dans le main car il contient tous les defines relatifs aux noms des controles crées. Exemple : -------- Main : --------- #include #include "resource.h" /* Proto callback sélection de rendez-vous */ BOOL CALLBACK SelRDV(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hWndDlg ; MSG msg ; hWndDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), (HWND) NULL, (DLGPROC)SelRDV) ; ShowWindow (HWNDDlg, SW_SHOW) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; } return msg.wParam ; } BOOL CALLBACK SelRDV(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : switch (LOWORD(wParam)) { case IDOK : case IDCANCEL : EndDialog (hDlg, 0) ; PostQuitMessage (0) ; // On envoie le WM_QUIT return TRUE ; } break ; } return FALSE; } ----- resource.h : ------- #define IDD_DIALOG1 101 #define IDC_STATIC -1 ----- script.rc : -------- #include #include "resource.h" IDD_DIALOG1 DIALOGEX 0, 0, 187, 101 STYLE DS_SYSMODAL | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_NOPARENTNOTIFY CAPTION "Dialog" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,130,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,130,24,50,14 LTEXT "Hello",IDC_STATIC,7,7,108,13 END Modifier la boite de dialogue : ----------------------------- LONG SetWindowLong(hDlg, nIndex, dwNewLong); avec nIndex : DWL_DLGPROC Nelle addresse de la procédure boite de dialogue. DWL_MSGRESULT Force la valeur de retour d'un message à procéder par la boite de dialogue. DWL_USER A la disposition de l'utilisateur. Récupérer des données d'un controle de boite : -------------------------------------------- x = GetDlgItemInt (hDlg, nIDDlgItem, NULL, bSigned); /* caster à (int) si signé */ n = GetDlgItemText(hDlg, nIDDlgItem, lpString, nMaxCount); /* n caractères lus */ Envoyer des données vers un controle de boite : --------------------------------------------- BOOL SetDlgItemInt (hDlg, nIDDlgItem, uValue, bSigned); /* -> 0 si echec */ BOOL SetDlgItemText(hDlg, nIDDlgItem, lpString); /* -> 0 si echec */ Cas de la liste (Combobox) : -------------------------- On peut dialoguer avec la combobox par : DispatchMessage (&msg) ; LONG SendDlgItemMessage(hDlg, nIDDlgItem, Msg, wParam, lParam); avec (UINT) Msg : CB_ADDSTRING : Ajouter un item à la liste. Renvoi l'index si Ok wParam = 0; lParam = (LPARAM) (LPCTSTR) lpsz; // addresse de la chaine à ajouter CB_SETCURSEL : Afficher l'item numéro index. Renvoi l'index si Ok wParam = (WPARAM) index; // index de l'item lParam = 0; CB_GETCURSEL : Obtenir l'index de l'item sélectionnée. Renvoi l'index si Ok ou CB_ERR si rien de sélectionné wParam = 0; lParam = 0; Un accès de l'usager à la liste combobox produit l'envoi du message : CBN_SELCHANGE idComboBox = (int) LOWORD(wParam); // identifier of combo box hWndComboBox = (hWnd) lParam; // handle to combo box ////////////////////////////////////////////////////////////////////////////////////// /////////////////// ACCEPTER LE DRAG-FILE /////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// A) Par exemple à la création de la fenêtre (traitement WM_CREATE) mettre : DragAcceptFiles(hWnd, TRUE); // Mettre FALSE dès qu'on n'en veut plus B) Traiter les messages de commande WM_DROPFILES case WM_DROPFILES : { char szNames[MAX_PATH]; UINT nDropped, n; nDropped = DragQueryFile((HANDLE) wParam,0xFFFFFFFF,NULL,0); // Recupère le nombre de fichiers if (nDropped) // Si des fichiers sont présents { SetActiveWindow(hWnd); // on active la fenêtre de l'appli for (n = 0; n < nDropped; n++) { // Recupère le nom de chaque fichier DragQueryFile((HANDLE) wParam, n, szNames, sizeof(szNames)); ............. FAIT CE QUE DOIT AVEC LE NOM ............. } } } DragFinish((HANDLE) wParam); /* Delete la structure allouée */ return 0 ; } Remarque : Si un seul fichier à récupérer, faire directement : DragQueryFile((HANDLE) wParam, 0, szNames, sizeof(szNames)); ............. FAIT CE QUE DOIT AVEC LE NOM ............. DragFinish((HANDLE) wParam); /* Delete la structure allouée */ return 0 ; /////////////////////////////////////////////////////////////////////////////////////// MESSAGES BOX OK, NO, ABORT ET COMPAGNIE /////////////////////////////////////////////////////////////////////////////////////// i = MessageBox(hWnd,"Appuyer sur Oui, Non ou Annuler", NULL, MB_YESNOCANCEL | MB_ICONINFORMATION); renvoi IDYES ou IDNO ou IDCANCEL. i = MessageBox(hWnd,"Appuyer sur Abandonner, Recommencer, Ignorer",NULL, MB_ABORTRETRYIGNORE | MB_ICONQUESTION); renvoi IDABORT ou IDRETRY ou IDIGNORE. /////////////////////////////////////////////////////////////////////////////////////// BOITE DE DIALOGUE OUVERTURE DE FICHIER /////////////////////////////////////////////////////////////////////////////////////// static char PathEtNomFich[_MAX_PATH] ; //(pas dans la pile !!) memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof (OPENFILENAME) ; ofn.hwndOwner = hWndProprio ; // Si NULL, Kill appli -> Fantôme ofn.lpstrFile = PathEtNomFich ; ofn.nMaxFile = sizeof(PathEtNomFich) ; if (GetOpenFileName(&ofn)) { \\ C'est bon : \\ PathEtNomFich contient le nom complet avec le path, le nom et l'extension \\ PathEtNomFich + ofn.nFileOffset pointe sur le nom seul avec l'extension \\ PathEtNomFich + ofn.nFileExtension pointe sur l'extension } /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// --------------------------------- DIVERS - DIVERS - DIVERS - DIVERS --------------------------------- Effacer un fichier : unlink("fichier"); /////////////////////////////////////////////////////////////////////////////////////// ENVIRONNEMENT: /////////////////////////////////////////////////////////////////////////////////////// Les strings d'environnement sont accesibles (si norme POSIX) à l'aide du tableau de string environ défini dans stdlib.h extern char ** environ; /* pointer to environment table */ /////////////////////////////////////////////////////////////////////////////////////// QUEL EST LE REPERTOIRE COURANT: /////////////////////////////////////////////////////////////////////////////////////// // Suppose #include getcwd (szBuffer, DIMBUF-1); //Renvoie un pointeur sur szBuffer si Ok, NULL si erreur. CHANGEMENT DE REPERTOIRE COURANT: chdir("d:\\tata\\toto") ; // Si d: n'était pas l'unité courante, elle le devient. chdir("tutu"); // Suppose que tutu est un sous-répertoire du répertoire courant chdir(".."); /////////////////////////////////////////////////////////////////////////////////////// UNICODE /////////////////////////////////////////////////////////////////////////////////////// INCREMENTATION D'UN POINTEUR DE CARACTERE pText = CharNext(pText); REMPLACEMENT D'UN CARACTERE DANS UNE CHAINE: *strchr(szString, '+') = '-'; //Remplace le 1er + par un -. /////////////////////////////////////////////////////////////////////////////////////// ENVOYER UN BEEP OU FLASHER LA FENETRE : /////////////////////////////////////////////////////////////////////////////////////// MessageBeep(0) ; FlashWindow(hWnd, bInvert); /////////////////////////////////////////////////////////////////////////////////////// TIMER /////////////////////////////////////////////////////////////////////////////////////// CREATION D'UN TIMER UINT SetTimer( HWND hWnd, // handle of window for timer messages (NULL possible) UINT nIDEvent, // timer identifier (ignoré si hWnd=NULL) permettant de le distinguer UINT uElapse, // time-out value (en ms) (réarmement automatique) TIMERPROC lpTimerFunc // address of timer procedure (NULL possible -> WM_TIMER à l'appli) ); Destruction d'un timer : BOOL KillTimer( HWND hWnd, // handle of window that installed timer UINT uIDEvent // timer identifier ); Acces au temps écoulé : GetLocalTime(SYSTEMTIME *ptime); //-> structure avec date et temps,.... clock_t clock( void ); // nombre de ticks depuis le démarrage il y a CLOCKS_PER_SEC ticks par seconde (defini dans time.h). A priori 1000 depuis VC 6.0 time_t time( time_t *timer ); // temps écoulé en secondes depuis le le 1/1/1970 double difftime( time_t timer1, time_t timer0 ); // différence de deux time_t BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); // -> frequence BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); // -> Nb ticks /////////////////////////////////////////////////////////////////////////////////////// CREATION D'UN AUTRE THREAD // NE PAS OUBLIER : Project / Settings / C/C++ / Code Generation / MultiThread /////////////////////////////////////////////////////////////////////////////////////// _beginthread (MyThread, 0, void *arglist) ; void MyThread (void *arglist) { ........ _endthread(); } IDentification du thread DWORD GetCurrentThreadId(VOID) ECHANGE D'EVENEMENT ENTRE THREAD static HANDLE hEventCR ; // Création de l'évènement hEventCR = CreateEvent (NULL, FALSE, FALSE, NULL) ; // Attente d'un retour du thread en question WaitForSingleObject(hEventCR, INFINITE) ; // Envoi du retour attendu SetEvent (hEventCR) ; // Mise d'un évènement dans la queue d'un thread BOOL PostThreadMessage( DWORD idThread, // thread identifier UINT Msg, // message to post WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); /////////////////////////////////////////////////////////////////////////////////// // Exemple de creation d'une console connaissant stdout, stderr; #if 0 #include #include #include #include int hCrt; FILE *hfo; FILE *hfe; FILE *hfi; #endif AllocConsole(); hCrt = _open_osfhandle((long) GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT); hfo = _fdopen(hCrt, "w"); hfe = _fdopen(hCrt, "w"); *stdout = *hfo; *stderr = *hfe; i = setvbuf( stdout, NULL, _IONBF, 0 ); i = setvbuf( stderr, NULL, _IONBF, 0 ); /* 6 - Distribuer les messages jusqu'à réception du message WM_QUIT (produit par un PostQuitMessage(0) par exemple. */ while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } FreeConsole(); /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// FENETRES WIN32 POUR OPENGL /////////////////////////////////////////////////////////////////////////////////////// 14.1 Le Pixel Format C'est un format qui décrit les caractéristiques 3D que l'on désire utiliser, avec en particulier le nombre de plans couleurs, le mode simple ou double buffer, la profondeur du Z buffer.. Windows offre 24 formats soft générique, mais la carte graphique peut offrir des formats hard (ICD : Installable Client Driver) ou hard-soft (MCD : Mini Client Driver). La procédure suivante permet de sélectionner le format hard (s'il existe) ou soft le plus proche de celui qui est désiré : void SetDCPixelFormat(HDC hDC) { static PIXELFORMATDESCRIPTOR pfd; int pf; pfd = { sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure 1, // Version of this structure PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap) PFD_SUPPORT_OPENGL | // Support OpenGL calls in window PFD_DOUBLEBUFFER, // Double buffered mode PFD_TYPE_RGBA, // RGBA Color mode 32, // Want 32 bit color 0,0,0,0,0,0, // Not used to select mode 0,0, // Not used to select mode 0,0,0,0,0, // Not used to select mode 16, // Size of depth buffer 0, // Not used to select mode 0, // Not used to select mode 0, // Not used to select mode 0, // Not used to select mode 0,0,0 }; // Not used to select mode // Choose a pixel format that best matches that described in pfd nPixelFormat = ChoosePixelFormat(hDC, &pfd); // Set the pixel format for the device context SetPixelFormat(hDC, pf, &pfd); } ////////////////////////////////////////////////////////////////////////////////////////////// MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS MEMOS ////////////////////////////////////////////////////////////////////////////////////////////// Pointeur et tableau en C ------------------------ int X[10][3] définit un tableau de 10 "tableaux de 3 entiers". Ce tableau "X" peut être passé en argument à une routine qui attent un int (*Y)[3], c'est-à-dire un pointeur sur un (ou plusieurs) tableau(x) de 3 entiers. On pourra accéder à un élement par Y[i][j] avec 0 <= i <= 9 et 0 <= j <= 2. Remarque : - "int *Y[3]" Y est un tableau de 3 pointeurs sur des int. - "int (*Y)[3]" Y est un pointeur sur des tableaux de 3 int. #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //////////////////////////////////////////////////////////////////////////////// EXECUTION DE PROCESS //////////////////////////////////////////////////////////////////////////////// Exécution d'un process en remplacement du process en cours : ----------------------------------------------------------- Si nombre d'arguments connu : int _execl ( const char *cmdname, const char *arg0, ... const char *argn, NULL ); int _execle ( const char *cmdname, const char *arg0, ... const char *argn, NULL, const char *const *envp ); Avec utilisation du path pour trouver le cmdname. int _execlp ( const char *cmdname, const char *arg0, ... const char *argn, NULL ); int _execlpe( const char *cmdname, const char *arg0, ... const char *argn, NULL, const char *const *envp ); Si nombre d'arguments variable : int _execv ( const char *cmdname, const char *const *argv ); int _execve ( const char *cmdname, const char *const *argv, const char *const *envp ); Avec utilisation du path pour trouver le cmdname. int _execvp ( const char *cmdname, const char *const *argv ); int _execvpe( const char *cmdname, const char *const *argv, const char *const *envp ); Création et exécution d'un nouveau process : -------------------------------------------- Si nombre d'arguments connu : int _spawnl ( int mode, const char *cmdname, const char *arg0, const char *arg1, ... const char *argn, NULL ); int _spawnle ( int mode, const char *cmdname, const char *arg0, const char *arg1, ... const char *argn, NULL, const char *const *envp ); Avec utilisation du path pour trouver le cmdname. int _spawnlp ( int mode, const char *cmdname, const char *arg0, const char *arg1, ... const char *argn, NULL ); int _spawnlpe( int mode, const char *cmdname, const char *arg0, const char *arg1, ... const char *argn, NULL, const char *const *envp ); Si nombre d'arguments variable : int _spawnv ( int mode, const char *cmdname, const char *const *argv ); int _spawnve ( int mode, const char *cmdname, const char *const *argv, const char *const *envp ); Avec utilisation du path pour trouver le cmdname. int _spawnvp ( int mode, const char *cmdname, const char *const *argv ); int _spawnvpe( int mode, const char *cmdname, const char *const *argv, const char *const *envp ); Argument mode : _P_OVERLAY : remplacement du process en cours par le nouveau : idem exec spawn synchro : _P_WAIT : suspension process en cours jusqu'à la fin du nouveau. spawn asynchrone : _P_NOWAIT : appel nouveau process en parallèle. _P_DETACH : lancement nouveau proces en background sans accès à la console. Mais également : WinExec("notepad.exe",SW_SHOW); ou BOOL CreateProcess(AppName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// DIRECT DRAW (C \ C++) //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// On crée d'abord un DirectDraw Object (DD) par DirectDrawCreate(..) qui renvoie une IdirectDraw interface qui permet d'accéder aux fonctionalités de l'objet qui est associé au périf (un objet par écran). Ces fonctions permettent de créer un DirectDrawSurface Object (DDS) par IdirectDraw2_CreateSurface(pDD,..) ou pDD->CreateSurface(..). On accède aux fonctionalités de la surface par les interfaces IDirectDrawSurface, IDirectDrawSurface2, et IDirectDrawSurface3. Si le pixel format > à 8 -> pas de palette. Sinon on utilise un DirectDrawPalette object crée par IdirectDraw2_CreatePalette(pDD,..) ou pDD->CreatePalette(..). On accède aux fonctionalités de la palette par les interfaces IDirectDrawPalette. Un DirectDrawClipper Object qui permet d'effectuer du clipping est crée par IdirectDraw2_CreateClipper(pDD,..) ou pDD->CreateClipper(..) Cooperative Levels : IdirectDraw2_SetCooperative(pDD,..) ou pDD->CreateClipper(..) permettent de fixer le mode d'action sur l'écran : plein écran ou mode fenêtre (DDSCL_NORMAL) et autre... Pour accéder aux nouvelles fonctionalités d'un objet apportées par ses nouvelles interfaces, il faut substituer la nlle interface à l'ancienne par la méthode QueryInterface de la précédente : Exemple C++ pour accéder à IdirectDraw2 : LPDIRECTDRAW lpDD; LPDIRECTDRAW2 lpDD2; if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK) return; if (lpDD->SetCooperativeLevel(hWnd,DDSCL_NORMAL) != DD_OK) return; if (lpDD->QueryInterface(IID_IDirectDraw2, (LPVOID *)&lpDD2) != DD_OK) return; Exemple C pour accéder à IdirectDraw2 : if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK) return; if (IDirectDraw_SetCooperativeLevel(lpDD,(hWnd,DDSCL_NORMAL) != DD_OK) return; if (IDirectDraw_QueryInterface (ddraw, &IID_IDirectDraw2,(LPVOID *)&lpDD2) != DD_OK) return; IDirectDraw_Release (lpDD); Exemple C++ pour accéder à IDirectDrawSurface3 : LPDIRECTDRAWSURFACE lpSurf; LPDIRECTDRAWSURFACE2 lpSurf3; DDSURFACEDESC ddds; memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; ddsd.dwWidth = 10; ddsd.dwHeight = 10; if (lpDD2->CreateSurface(&ddsd, &lpSurf,NULL) != DD_OK) return; if (lpSurf->QueryInterface(IID_IDirectDrawSurface3, (LPVOID *)&lpSurf3) != DD_OK) return; if (lpSurf3->PageLock(0) != DD_OK) return; if (lpSurf2->PageUnlock(0) != DD_OK) return; Exemple C pour accéder à IDirectDrawSurface2 : LPDIRECTDRAWSURFACE lpSurf; LPDIRECTDRAWSURFACE2 lpSurf3; DDSURFACEDESC ddds; memset (&ddsd, 0, sizeof (ddds)); ddsd.dwSize = sizeof (ddds); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; if (IDirectDraw2_CreateSurface (lpDD2, &ddsd, &lpSurf, NULL) != DD_OK) return; if (IDirectDrawSurface_QueryInterface (lpSurf, &IID_IDirectDrawSurface2, (LPVOID *) &lpSurf2) != DD_OK) return; IDirectDrawSurface_Release (lpSurf); Propriétés la surface : ---------------------- On fixe les propriétés désirées de cette surface en paramétrant le Direct DRaw Surface Descriptor {DDSURFACEDESC ddsc;}. On renseigne d'abord sa taille (du descripteur) et les flags associés aux paramètres qu'on veut fixer. Ex : memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; Puis on renseigne les paramètres choisis : ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; ddsd.dwWidth = 10; ddsd.dwHeight = 10; Par défaut les surfaces sont créées dans la carte graphique (DDSCAPS_VIDEOMEMORY) ou en mémoire système (DDSCAPS_SYSTEMMEMORY), ce qui est souvent mieux pour les surfaces secondaires. On crée une seule surface primaire (DDSCAPS_PRIMARYSURFACE) et des surfaces secondaires (DDSCAPS_OFFSCREENPLAIN) dans lequelles on prépare les images qui seront commutées (blitées) sur la surface primaire. On peut créer simultanément plusieurs surfaces (DDSCAPS_COMPLEX) qui font partie d'une flipping chaîne (DDSCAPS_FLIP). On crée une surface overlay (forcement en mémoire video) par DDSCAPS_OVERLAY. Une surface overlay est directement lue lors du balayage de la ligne (il y a un saut vers le rectangle overlay, puis retour à la surface périf), sans transfert de bits (les bits de la surface périf ne sont pas affectés). L'overlay est mis en place par ->UpdateOverlay() et repositionné par ->SetOverlayPosition() Access direct à un rectangle d'une surface : ------------------------------------------ La méthode lpSurfX->Lock(Rect, &ddsd,...) en C++ et IDirectDrawSurfaceX_Lock(lpSurfX,Rect, &ddsd,...) en C renseigne le DDSURFACEDESC ddsc avec tous les éléments nécessaires pour effectuer un accès directà la surface : pitch ou stride (largeur globale incluant le cache), ... (utiliser le flag DDLOCK_WAIT pour éviter les pb). XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX DIRECTIVES PRAGMA : De nombreuses directives qui permettent de modifier le comportement du compilateur et du linker XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Pour indiquer au linker un nom de lib à lier : #pragma comment (lib, "glu32.lib") /* link with OpenGL Utility lib */ Pour rappeler quelque chose dans le log de la compilation : #pragma message( "FonctionToto(): Il y a encore du travail à faire ici!" ) Pour désactiver des warnings : #pragma warning(push) /* Mémorise l'état actuel des warnings */ #pragma warning(disable:4002) // Trop de paramètres pour la macro #pragma warning(disable:4035) // Manque la return value. #pragma warning(disable:4200) // Tableau de dim nulle dans une struct #pragma warning(disable:4244) // Conversion de type avec perte possible t1 -> t2 #pragma warning(disable:4305) // Conversion de type avec troncature t1 -> t2 #pragma warning(disable:4705) // Instruction sans effet #pragma warning(disable:4799) // function 'foo' has no EMMS instruction #pragma warning(disable:4511 4512 4514) ..... #pragma warning(default:4035) // restaure ce warning #pragma warning(pop) /* restore l'ancien état des warnings Pour modifier les alignements en mémoire des membres des structures : #pragma pack(push, 2) /* Entre ces 2 pragma les struct sont alignés au niveau 2 octets (word) #pragma pack(pop) /* Ici elles retrouvent leur alignement initial */ Warning 4244