Tutorial 18: Commandes Communes
(Common Controls)

Nous allons voir quelles sont les commandes les plus utilisées et voir comment on les emploie. Ce Tutorial sera seulement une rapide introduction à celles-ci.
Downloadez l'exemple ici.

Théorie:

Windows 95 a été doté de plusieurs nouveautés d'interface d'utilisateur que n'avait pas Windows 3.1x. Ceci enrichi encore le GUI (NdT : Graphic User Interface). Plusieurs d'entre eux sont largement employés avant même que Windows 95 ne s'affiche à l'écran, comme la barre de statut, des barres d'outils etc. Les programmeurs doivent les coder eux-mêmes. Maintenant Microsoft les a inclus dans Windows 9x et NT. Nous allons les voir ici.
Voici ces nouvelles commandes : Voici une adresse ou vous trouverez de petits programmes d'exemples de chacun de ces Common Control. Ils ont étés codés en C++ et non pas en MASM mais le principe est là. Vous comprendrez ce que chacun d'eux représente.
http://www.microsoft.com/msj/defaulttop.asp?page=/msj/0798/controlspytop.htm
(Merci à Sharpsighted pour l'info.               sharpsighted@caramail.com)


Puisque il y en a beaucoup, le fait de les charger en mémoire et les déclarer serait une perte de ressource. Tous, à l'exception du contrôle d'édition, sont stockés dans comctl32.dll. Et n'importe quel programme peut les charger quand il a besoin d'employer une de ces commandes. Le contrôle d'édition (riche ou évolué)réside dans son propre dll, richedXX.dll, parce qu'il est très compliqué et plus volumineux que ses frères.
Vous pouvez charger comctl32.dll grâce à l'inclusion d'un Call InitCommonControls dans votre programme. InitCommonControls est une fonction de comctl32.dll, donc en vous y référant n'importe où dans votre code, fera que le Loader PE chargera comctl32.dll quand votre programme est en court. Vous ne devez pas l'exécuter, incluez la simplement quelque part dans votre code. Cette fonction ne fait RIEN! Sa seule et unique instruction est "ret". Son seul but c'est d'inclure la référence de comctl32.dll dans la section d'importation pour que le Loader PE le charge à chaque fois que le programme est chargé. Le vrai boulot se fait au niveau de l'entrypoint de la fonction DLL qui enregistre toutes les classes de contrôles communs (common control classes) quand le dll est chargé. Les 'Commons Controls' sont créées sur la base de ces classes, tout comme d'autre 'child window controls' tels que la zone d'édition, la liste etc.
Pour le contrôle d'édition évolué (riche) c'est une autre histoire. Si vous voulez l'employer, vous devez appeler LoadLibrary pour le charger et appeler FreeLibrary pour le décharger.
Maintenant nous allons voir comment les créer. Vous pouvez employer un éditeur de ressources pour les incorporer dans des boîtes de dialogue ou bien vous pouvez les créer vous-même. Presque tous les common controls sont créées en appelant CreateWindowEx ou CreateWindow, en leur passant le nom de la classe du contrôle. Quelques common controls ont des fonctions de création spéciales, cependant, ce ne sont que des broutilles autour de CreateWindowEx pour rendre la création de ces 'controls' plus simple. Ces fonctions spécifiques de création sont inscrites ci-dessous :
Pour pouvoir créer ces 'common controls', vous devez connaître leurs noms de classe. Les voici inscrits ci-dessous :
 
Class Name
Common Control
ToolbarWindow32 Toolbar
tooltips_class32 Tooltip
msctls_statusbar32 Status bar
SysTreeView32 Tree view
SysListView32 List view
SysAnimate32 Animation
SysHeader32 Header
msctls_hotkey32 Hot-key
msctls_progress32 Progress bar
RICHEDIT Rich edit
msctls_updown32 Up-down
SysTabControl32 Tab
Les 'Property sheets', les 'property pages' et l 'image list control' (soit les feuilles de propriété, les pages de propriété, et le contrôle de liste d'image) ont leurs propres fonctions spécifiques de création. La 'Drag list control' gonfle encore la liste donc elle n'a pas sa propre classe. Ces susdits noms de classe sont vérifiés en regardant le scénario de ressource, produit par l'éditeur de ressource Visuel C++. Ils diffèrent des noms de classe inscrits par win32 de la référence Borland api et de ceux de la Programmation Windows de Charles Petzold 95. La liste est précisément celle-ci.
Ces common controls peuvent utiliser les styles généraux de fenêtres tel que WS_CHILD etc. Ils ont aussi leurs propres styles spécifiques comme TVS_XXXXX pour la 'tree view control' (contrôle montrant les fichiers sous forme d'arbre ), LVS_xxxx pour la 'list view control' (contrôle montrant les fichiers sous forme d'une liste ), etc. La référence d' api Win32 sera votre meilleur ami à cet égard.
Maintenant que nous savons comment créer des common controls, nous pouvons nous intéresser à la méthode de communication entre ces common controls et leurs parents. À la différence des 'child window controls', les 'common controls' ne communique pas avec leur parent avec des messages WM_COMMAND. Au lieu de ça, ils envoient des messages WM_NOTIFY à la fenêtre parente lorsque certains événements bien précis arrivent. Le parent peut contrôler les enfants en leur envoyant des messages. Il y a aussi beaucoup de nouveaux messages pour ces nouvelles commandes. Vous devez consulter votre référence api win32 pour plus de détail.
Dans l'exemple suivant, on va examiner la barre de progression et les commandes de barre de statut. ('progress bar' et 'status bar controls')

échantillon de code:

 .386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.const
IDC_PROGRESS equ 1            ; Les n° d'Identités des 'controls' (par exemple n°ID de la barre de progression)
IDC_STATUS equ 2
IDC_TIMER equ 3

.data
ClassName db "CommonControlWinClass",0
AppName   db "Common Control Demo",0
ProgressClass db "msctls_progress32",0       ; La 'class name' de la barre de progression
Message db "Finished!",0
TimerID dd 0

.data?
hInstance HINSTANCE ?
hwndProgress dd ?
hwndStatus dd ?
CurrentStep dd ?
.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax
    invoke InitCommonControls

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_APPWORKSPACE
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    .while TRUE
         invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw
    mov eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_CREATE
         invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
            WS_CHILD+WS_VISIBLE,100,\
            200,300,20,hWnd,IDC_PROGRESS,\
            hInstance,NULL
        mov hwndProgress,eax
        mov eax,1000       ; Le paramètre lParam du message PBM_SETRANGE contient la gamme, (en général de 0 à 100)
        mov CurrentStep,eax
        shl eax,16                   ; La haute de la gamme (ici 100) est dans le mot de poids fort
        invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
        invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0
        invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
        mov hwndStatus,eax
        invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; crée un minuteur (un timer)
        mov TimerID,eax
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
        .if TimerID!=0
            invoke KillTimer,hWnd,TimerID
        .endif
    .elseif uMsg==WM_TIMER        ; Quand un 'timer event' survient
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0    ; Remplissage (pas à pas) de la barre de progression.
        sub CurrentStep,10
        .if CurrentStep==0
            invoke KillTimer,hWnd,TimerID
            mov TimerID,0
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
            invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
            invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
        .endif
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
end start

Analyse:

J'ai délibérément mis InitCommonControls après ExitProcess pour vous faire voir que InitCommonControls est juste ici pour la mettre en référence comctl32.dll, dans la section d'importation. Comme vous pouvez le voir, les 'common controls' fonctionnent parfaitement même lorsque InitCommonControls n'est pas exécuter. Voici l'endroit où nous créons le common control. Notez que cet appel de CreateWindowEx contient la variable 'hWnd' en tant que 'handle de la fenêtre parente'. On spécifie aussi un 'Control ID' pour l'identification de ce contrôle. Cependant, puisque nous avons l' handle du 'control', cet ID n'est pas employé. Tout 'child window control' doit avoir le style WS_CHILD. Après que la barre de progression est été créée, nous pouvons définir sa gamme. La gamme par défaut va de 0 à 100. Si vous n'en êtes pas satisfaits, vous pouvez spécifier votre propre gamme avec le message PBM_SETRANGE. Le message 'lParam' contient la gamme, la gamme maximale (ici 100d ou 64h) est placée dans le mot de poids fort alors que le minimum (0) est dans le mot de poids faible. Vous pouvez spécifier le 'nombre de pas' dont cette barre sera remplie, en employant PBM_SETSTEP. Dans notre exemple, on le met à 10 ce qui signifie que lorsque un message PBM_STEPIT est envoyé à la barre de progression, l'indicateur de progression montera de 10 par 10. Mais vous pouvez aussi définir le niveau de l'indicateur en envoyant des messages PBM_SETPOS. Ce message vous donne un contrôle plus précis sur la barre de progression. Ensuite, nous créons une barre de statut en appelant CreateStatusWindow. C'est un appel facile à comprendre donc je ne ferai pas de remarques. Après que la fenêtre de statut ait été créée, nous fabriquons un minuteur. Dans cet exemple, nous mettrons à jour la barre de progrès dans un intervalle régulier de 100ms, donc nous devons créer un contrôle de minuteur (un control timer). Ci-dessous voici le 'prototype de fonction' de SetTimer. hWnd : handle de la fenêtre Parente
TimerID : Le n° d'Identité du minuteur (du Timer), différent de zéro. Vous pouvez mettre le n° que vous voulez pour l'identifier. Mais si vous avez plusieurs Timers , chacun doit avoir son propre n° d'ID
TimerInterval : L'intervalle de temps du minuteur, en millisecondes, doit être terminé avant que le minuteur appelle la procédure du minuteur ou envoie un message WM_TIMER
lpTimerProc : L'adresse de la fonction minuteur (la fonction de temps) qui sera appelée lorsque l'intervalle de temps est expiré. Si ce paramètre est NULL, le minuteur enverra le message WM_TIMER à la fenêtre parente, au lieu de cette adresse.

Si cet appel est couronné de succès, il renverra le TimerID. S'il a échoué, il retourne la valeur 0. C'est pourquoi la valeur du TimerID doit absolument être différente de zéro.

Quand l'intervalle de temps indiqué expire, le minuteur (le timer) envoie le message WM_TIMER. Vous pourrez placer votre propre code ici pour qu'il soit exécuté. Dans cet exemple, nous mettons à jour la barre de progression et vérifions ensuite si la limite maximale a été atteinte (dépassée). Si c'est le cas, nous fermons le Timer et mettons ensuite le texte dans la fenêtre de statut avec le message SB_SETTEXT. Une boîte de message est affichée, et quand l'utilisateur clique sur OK, nous purifions le texte de la barre de statut ainsi que la barre de progression.

[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte