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: |
|
| Language: | Italian |
| 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
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
.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 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.
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 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.
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.
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.
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.