Tutorial 22: Superclassing
From UIC
Tutorial 22: Superclassing
Contents |
| Tutorial 22: Superclassing | |
|---|---|
| Author: | Iczelion |
| Email: | Translate: nOtEtA |
| Website: | Mirror |
| Date: | 12/02/2009 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
In questo tutorial, impareremo circa il superclassing, che cos' è e che cosa fa. Imparerete anche come fornire il percorso del tasto Tab tra i controlli nella vostra finestra.
Tools
Essay
Nella vostra carriera di programmazione, certamente incontrerete una situazione dove avrete bisogno di parecchi controlli con un compartamento * un po' * differente. Per esempio, potreste avere bisogno di 10 casella di modifica che accettino soltanto numeri. Ci sono parecchi modi per realizzare quest' obiettivo:
- Creare il vostro codice per la classe ed usarlo al posto dei controlli;
- Creare queste casella di modifica ed allora subclassarle tutte;
- Superclassare le caselle di modifica.
Il primo metodo è troppo noioso. Dovete effettuare voi ogni funzionalità della casella di modifica. Un'operazione da non prendere sottogamba. Il secondo metodo è migliore di quello precedente ma ancora di troppo lavoro. È giusto se subclassate soltanto alcuni controlli ma sarebbe un incubo subclassare una dozzina circa di controlli. Il superclassing è la tecnica che dovreste usare per questa situazione. Il superclassing è il metodo che usate per * prendere il controllo * di una categoria particolare di finestre. * Prendere il controllo *, significa che potete modificare le proprietà di una categoria di finestre per soddisfare il vostro scopo e quindi creare un' insieme di controlli. I punti del superclassing sono descritto qui sotto:
- chiamate GetClassInfoEx per ottenere le informazioni sulla categoria di finestre che desiderate superclassare. GetClassInfoEx richiede un puntatore ad una struttura WNDCLASSEX che sarà riempita di informazioni se la chiamata ritorna con successo.
- Modificate i membri di WNDCLASSEX che volete. Comunque, ci sono due membri che DOVETE modificare:
1) hInstance Dovete mettere l' handle dell' istanza del vostro programma in questo membro.
2) lpszClassName Dovete fornire un puntatore ad un nuovo nome della classe. Non è necessario che modifichiate il membro lpfnWndProc ma la maggior parte delle volte, dovrete farlo. Ricordatevi di salvare il valore originale di lpfnWndProc se desiderate chiamarlo con CallWindowProc.
- Registrate le modifiche alla struttura WNDCLASSEX. Avrete una nuova classe di finestre che avrà parecchie caratteristiche della vecchia classe di finestre.
- Create le finestre dalla nuova classe.
Il Superclassing è migliore del subclassing se desiderate creare molti controlli con le stesse caratteristiche.
Esempio
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WM_SUPERCLASS equ WM_USER+5
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SuperclassWinClass",0
AppName db "Superclassing Demo",0
EditClass db "EDIT",0
OurClass db "SUPEREDITCLASS",0
Message db "You pressed the Enter key in the text box!",0
.data?
hInstance dd ?
hwndEdit dd 6 dup(?)
OldWndProc dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
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+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR AppName,
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,
CW_USEDEFAULT,350,220,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 uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL wc:WNDCLASSEX
.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
invoke RegisterClassEx, addr wc
xor ebx,ebx
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,
WS_CHILD+WS_VISIBLE+WS_BORDER,20,
edi,300,25,hWnd,ebx,
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
.if uMsg==WM_CHAR
mov eax,wParam
.if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
.if al>="a" && al<="f"
sub al,20h
.endif
invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
ret
.endif
.elseif uMsg==WM_KEYDOWN
mov eax,wParam
.if al==VK_RETURN
invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
invoke SetFocus,hEdit
.elseif al==VK_TAB
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.endif
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
EditWndProc endp
end start
Analisi
Il programma creerà una finestra semplice con 6 caselle di modifica " alterate " nella relativa zona client. Le caselle di modifica accetteranno soltanto le cifre hex. In realtà, ho modificato l'esempio del subclassing per fare il superclassing. Il programma comincia normalmente e la parte interessante è quando la finestra principale viene creata:
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
Dobbiamo in primo luogo riempire la struttura WNDCLASSEX con i dati della classe che desideriamo superclassare, in questo caso, essa è la classe EDIT. Ricordatevi che dovete settare il membro cbSize della struttura WNDCLASSEX prima di chiamare GetClassInfoEx altrimenti la struttura WNDCLASSEX non sarà riempita correttamente. Dopo che GetClassInfoEx ritorna, wc è riempita con tutte le informazioni per creare una nuova classe di finestre.
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
Adesso dobbiamo modificare alcuni membri di wc. Il primo è il puntatore alla procedura per la finestra. Successivamente dobbiamo sostituire la nostra procedura per la finestra al posto di quella originale, dobbiamo salvarne il valore precedente in una variabile così che potremo chiamarlo con CallWindowProc. Queta tecnica è identica al subclassing eccetto che voi modificate la struttura WNDCLASSEX direttamente senza dover chiamare SetWindowLong. I seguenti due membri devono essere cambiati altrimenti non sarete abilitati a registrare la vostra nuova classe di finestre, hInstance e lpsClassName. Dovete rimpiazzare il valore originale di hInstance con l' hInstance del vostro programma. E dovete scegliere un nuovo nome per la nuova classe.
Quando tutto è pronto, registrate la nuova classe. Otterrete una nuova classe con alcune caratteristiche della vecchia classe.
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,
WS_CHILD+WS_VISIBLE+WS_BORDER,20,
edi,300,25,hWnd,ebx,
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
Ora che abbiamo registrato la classe, possiamo creare le finestre basate su essa. Nel precedente snippet, uso ebx come contatore del numero di finestre create. edi è usato come coordinata y dell' angolo superiore sinistro della finestra. Quando una finestra viene creata, la relativa handle viene memorizzata in un array di dwords. Quando tutte le finestre sono state create, regoliamo il fuoco per l'input sulla prima finestra. A questo punto, avete ottenuto 6 casella di modifica che accettano soltanto le cifre hex. La proc sostitutiva delle finestre gestisce il filtro. Realmente, è identica alla proc delle finestre nell'esempio del subclassing. Come potete vedere, non dovete fare il lavoro supplementare del subclassing.
Inserisco uno snippet di codice per maneggiare il percorso del tab per rendere questo esempio più sugoso. Normalmente, se mettete i controlli su una finestra di dialogo, la finestre di dialogo gestisce i tasti di percorso per voi in modo che voi possono usare il tab per andare al successivo controllo o lo shift-tab per andare di nuovo al controllo precedente. Viceversa, tale caratteristica non è disponibile se mettete i vostri controlli su una finestra semplice. Dovete subclassarli in modo da poter gestire il tasto Tab voi stessi. Nel nostro esempio, non abbiamo bisogno di subclassare i controlli uno per uno perché sono già superclassati, in modo che possiamo fornire un " gestore centrale della navigazione " per loro.
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
Il suddetto snippet di codice proviene dalla procedura EditWndClass. Controlla se l'utente preme il tasto Tab, in caso affermativo, esso chiama GetKeyState per controllare se il tasto SHIFT è premuto. GetKeyState restituisce un valore in eax che determina se il tasto specificato è premuto oppure no. Se il tasto è premuto, il bit più alto di eax è settato. Altrimente, il bit più alto è azzerato. Così verifichiamo il valore di ritorno con 80000000h. Se il bit più alto è settato, significa che l'utente sta premendo shift+tab e dobbiamo gestirlo separatamente. Se l'utente preme solo il tasto Tab, noi chiamiamo GetWindow per richiamare l' handle del controllo seguente. Usiamo il flag GW_HWNDNEXT per dire a GetWindow di ottenere l' handle della finestra che è la seguente in linea all' hEdit corrente. Se questa funzione restituisce NULL, lo interpretiamo come se non ci siano più handle da ottenere così l' hEdit corrente è l' ultimo controllo in linea. Noi " salteremo in circolo " al primo controllo chiamando GetWindow con il flag GW_HWNDFIRST. Similmente al caso del Tab, shift-tab funzionerà giustamente al contrario.
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.