Zoom Icon

Tutorial 18: I Controlli Comuni

From UIC

Tutorial 18: I Controlli Comuni

Contents


Tutorial 18: I Controlli Comuni
Author: Iczelion
Email: Translate: -NeuRaL_NoiSE
Website: Mirror
Date: 27/01/2009 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: Formattazione Wiki: Antelox


Introduzione

Impareremo cosa sono i controlli comuni (Common Controls) e come usarli. Questo tutorial sara' semplicemente una breve introduzione ad essi. Notate che questo esempio usa windows.inc dal package di MASM32. Ricordo che MASM32 potete scaricarlo da qui


Tools

Esempio


Essay

Windows 95 contiene diversi miglioramenti nell'interfaccia utente rispetto a Windows 3.1x. Essi rendono la GUI piu' ricca. Molti di questi erano gia' largamente usati prima che Windows 95 arrivasse nei negozi, come la status bar, le toolbars etc. I programmatori dovevano programmarli da soli. Ora la Microsoft li ha inclusi in Windows 9x e NT. Li tratteremo qui. Questi sono i nuovi controlli:

  • Toolbar
  • Tooltip
  • Status bar
  • Property sheet
  • Property page
  • Tree view
  • List view
  • Animation
  • Drag list
  • Header
  • Hot-key
  • Image list
  • Progress bar
  • Right edit
  • Tab
  • Trackbar
  • Up-down

Visto che ce ne sono diversi, caricarli tutti contemporaneamente in memoria e registrarli sarebbe uno spreco di risorse. Tutti questi controlli, ad eccezione del rich edit control, sono contenuti in comctl32.dll che le applicazioni possono caricare quando vogliono usare i controlli. Il rich edit control risiede nella sua propria dll, richedXX.dll, poiche' e' molto ingombrante e complicato. Potete caricare comctl32.dll includendo una call a InitCommonControls nel vostro programma. InitCommonControls e' una funzione in comctl32.dll, cosi', riferendosi ad essa in un punto qualsiasi nel vostro codice, farete in modo che il PE loader carichi comctl32.dll quando il vostro programma parte. Non dovete eseguirla, semplicemente includetela nel vostro codice da qualche parte. Questa funzione non fa NIENTE! La sua unica istruzione e' "ret". Il suo unico scopo e' includere una referenza a comctl32.dll nella import section cosicche' il PE loader la carichi quando il progamma viene eseguito. Il vero cavallo di battaglia e' la funzione di entrypoint della DLL che registra tutte le classi quando la dll e' caricata. i controlli comuni vengono creati basandosi su quelle classi proprio come gli altri controlli di child window come edit, listbox etc. Il rich edit e' tutto un altro discorso. Se volete usarlo, dovete chiamare LoadLibrary per caricarlo esplicitamente e poi chiamare FreeLibrary per scaricarlo. Ora sappiamo come crearli. Potete usare un editor di risorse per incorporarli nei dialog boxes o potete crearli da soli. Praticamente tutti i controlli comuni vengono creati chiamando CreateWindowEx o CreateWindow, a cui viene passato il nome della classe del controllo. Alcuni controlli comuni hanno funzioni di creazione specifiche, che comunque sono solo dei wrappers (un po' come un file batch che chiama un exe con dei parametri, NdT) attorno a CreateWindowEx per rendere piu' semplice la creazione di tali controlli. Le funzioni di creazione esistenti sono elencate qui sotto:

  • CreateToolbarEx
  • CreateStatusWindow
  • CreatePropertySheetPage
  • PropertySheet
  • ImageList_Create

Per creare controlli comuni, dovete conoscere i nomi delle loro class. Sono elencati qui:


Nome Class 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


I controlli property sheets e property pages, e image list hanno le loro specifiche funzioni di creazione. Il controllo drag list e' una listbox modificata quindi non ha la propria classe. I precedenti nomi di class sono verificati controllando lo script di risorse generato dall'editor di risorse del Visual C++. Essi differiscono dai nomi delle class elencati nella win32 api reference della Borland e in Programming Windows 95 di Charles Petzold. La lista che ho inserito qui e' quella accurata. Questi controlli comuni possono usare stili generici di finestre come WS_CHILD etc. Inoltre hanno stili specifici come TVS_XXXXX per il tree view control, LVS_xxxx per il list view control, etc. La Win32 api reference e' la vostra migliore amica per quanto riguarda questo. Ora che sappiamo come creare i controlli comuni, possiamo spostarci sui metodi di comunicazione tra i controlli comuni e i loro genitori. A differenza dei controlli di child window, i controlli comuni non comunicano con i genitori via WM_COMMAND. Al contrario, mandano messaggi WM_NOTIFY alla parent window quando qualche evento interessante avviene per i controlli comuni. Il genitore puo' controllare i figli mandandogli messaggi. Ci sono anche molti nuovi messaggi per questi nuovi controlli. Dovreste consultare la vostra win32 api reference per maggiori dettagli. Esaminiamo i controlli progress bar e status bar nell'esempio seguente.


CODICE CAMPIONE

.386
.model flat,stdcall
include windows.inc
include user32.inc
include kernel32.inc
include comctl32.inc
includelib comctl32.lib
includelib user32.lib
includelib kernel32.lib

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

.const
IDC_PROGRESS equ 1            ; control IDs
IDC_STATUS equ 2
IDC_TIMER equ 3

.data
ClassName  db "CommonControlWinClass",0
AppName    db "Common Control Demo",0
ProgressClass  db "msctls_progress32",0        ; il class name della progress bar
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:SDWORD
    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  hInstance
    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,0
    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
    mov   eax,uMsg
    .if eax==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                ; il lParam del messaggio PBM_SETRANGE contiene il raggio
        mov CurrentStep,eax
        shl eax,16                    ; l'high range e' nella high word
        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        ; crea un timer
        mov TimerID,eax
    .elseif eax==WM_DESTROY
        invoke PostQuitMessage,NULL
        .if TimerID!=0
            invoke KillTimer,hWnd,TimerID
        .endif
    .elseif eax==WM_TIMER        ; quando un avviene un timer event
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0    ; aumenta il progresso nella progress bar
        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


Analisi

invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
          invoke ExitProcess,eax
          invoke InitCommonControls

Ho deliberatamente messo InitCommonControls dopo ExitProcess per dimostrare che InitCommonControls e' li' solo per dare un riferimento a comctl32.dll nella import section. Come potete vedere, i controlli comuni funzionano anche se InitCommonControls non viene eseguito.

.if eax==WM_CREATE
               invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
                  WS_CHILD+WS_VISIBLE,100,\
                  200,300,20,hWnd,IDC_PROGRESS,\
                  hInstance,NULL
              mov hwndProgress,eax

Qui e' dove creiamo il controllo comune. Notate che questa call a CreateWindowEx contiene come hWnd quello della parent window. Essa specifica anche una control ID per identificare questo controllo. Comunque, visto che abbiamo il window handle del controllo, questa ID non e' usata. Tutti i controlli di child window devono avere lo stile WS_CHILD.

mov eax,1000
              mov CurrentStep,eax
              shl eax,16
              invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
              invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0

Dopo che la progress bar e' stata creata, ne settiamo il raggio. Il raggio predefinito e' da 0 a 100. Se non vi soddisfa, potete specificare il vostro raggio con il messaggio PBM_SETRANGE. Il lParam di questo messaggio contiene il raggio, quello massimo nella high word e quello minimo nella low word. Potete specificare quanto tempo richiede un singolo passo usando il messaggio PBM_SETSTEP. L'esempio lo setta a 10 che significa che quando mandate un messaggio PBM_STEPIT alla progress bar, l'indicatore di progresso aumentera' di 10. Potete anche configurare il vostro proprio livello di indicatore mandando messaggi PBM_SETPOS. Questo messaggio vi da' maggiore controllo sulla progress bar.

invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
              mov hwndStatus,eax
              invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; crea un timer
              mov TimerID,eax

Successivamente, creiamo una status bar chiamando CreateStatusWindow. Questa call e' semplice da capire percio' non la commentero'. Dopo che la status window e' creata, creiamo un timer. In questo esempio, aggiorneremo la progress bar ad un intervallo regolare di 100 ms, percio' dobbiamo creare un controllo timer. Qui sotto c'e' il prototipo di funzione per SetTimer.

SetTimer PROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerProc:DWORD

hWnd : handle della parent window

TimerID : un identificatore di timer (nonzero). Potete creare il vostro identificatore personale.

TimerInterval : l'intervallo in millisecondi che deve passare prima che il timer chiami la timer procedure o mandi un messaggio WM_TIMER

lpTimerProc : l'indirizzo della timer function che sara' chiamata quando l'intervallo del timer e' passato. Se invece questo parametro e' NULL, il timer mandera' un messaggio WM_TIMER alla parent window.

Se questa call riesce, la funzione restituisce il TimerID. Se fallisce, restituisce 0. E' per questo che il timer identifer deve essere diverso da zero.

.elseif eax==WM_TIMER
              invoke SendMessage,hwndProgress,PBM_STEPIT,0,0
              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

Quando l'intervallo di tempo specificato termina, il timer manda un messaggio WM_TIMER. Metterete il codice che deve essere eseguito qui. In questo esempio, aggiorniamo la progress bar e quindi controlliamo se il limite massimo e' stato raggiunto. Se si, eliminiamo il timer e quindi settiamo il testo nella status window con il messaggio SB_SETTEXT. Una message box viene mostrata e quando l'utente clicka OK, cancelliamo il testo nella status bar e la progress bar.


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.