Tutorial 20: Window Subclassing
From UIC
Tutorial 20: Window Subclassing
Contents |
| Tutorial 20: Window Subclassing | |
|---|---|
| Author: | Iczelion |
| Email: | Translate: nOtEtA |
| Website: | Mirror |
| Date: | 03/02/2009 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
In questo tutorial, impareremo circa il subclassing di finestra, che cosa è e come usarlo a nostro vantaggio.
Tools
Essay
Se programmate sotto Window da un certo tempo, troverete alcuni casi dove una finestra ha quasi gli attributi di cui avete bisogno nel vostro programma ma non tutti. Avete incontrato una situazione dove desideravate un certo genere speciale di casella di modifica che potesse filtrare fuori un certo testo indesiderato? La cosa diretta da fare è di scrivere il codice della vostra finestra. Ma è un lavoro duro e che richiede realmente del tempo. Il subclassing di una finestra ci può aiutare. In una nutshell, il subclassing di finestre permette di " prendere il controllo " della finestra subclassata. Avrete il controllo assoluto su di essa. Facciamo un esempio per capire meglio. Supponete di avere bisogno di una casella di testo che accetti soltanto i numeri esadecimali. Se usate una semplice casella di modifica, voi non sapete cosa e quando l' utente digita qualcosa oltre ai numeri hex nella vostra casella di testo, ie se l'utente digita " zb+q * " nella vostra casella di testo, non potete fare qualche cosa con essa tranne che il rifiuto della stringa di testo intera. Ciò non è professionale. Essenzialmente, avete bisogno della capacità di esaminare ogni carattere che l'utente ha digitato nella casella di testo nel momento in cui l'ha digitato. Esamineremo ora come fare questo. Quando l'utente digita qualcosa in una casella di testo, Window trasmette il messaggio WM_CHAR alla procedura della finestra casella di modifica. Questa procedura risiede all'interno di Window in modo da non poterla modificare. Ma possiamo ridirezionare il flusso dei messaggi alla nostra procedura per la finestra. In modo che la nostra procedura per la finestra ottenga per prima tutti i messaggi che Window trasmette alla casella di modifica. Se la nostra procedura per la finestra sceglie di intervenire sul messaggio, può farlo così. Ma se non desidera maneggiare il messaggio, può passarlo alla procedura originale di Window. In questo modo, la nostra procedura per la finestra si inserisce fra Window e la casella di modifica. Guardare il flusso qui sotto:
Prima del Subclassing
Windows ==> procedura per la finestra casella di modifica
Dopo il Subclassing
Windows ==> nostra procedura per la finestra -----> procedura di Window per la casella di modifica
Ora incentriamo la nostra attenzione su come fare il subclass a una finestra. Si noti che il subclassing non è limitato ai controls, può essere usato con tutte le finestre. Pensiamo a come Window sa dove la procedura per la finestra casella di modifica risiede. Un' ipotesi?.....lpfnWndProc della struttura WNDCLASSEX. Se possiamo sostituire questo membro con l'indirizzo della nostra procedura per la finestra, Window trasmetterà preferibilmente i messaggi alla nostro proc per la finestra. Possiamo fare questo chiamando SetWindowLong.
hWnd = handle della finestra per cambiare il valore nella struttura WNDCLASSEX
nIndex == valore da cambiare.
GWL_EXSTYLE Setta un nuovo stile esteso per la finestra.
GWL_STYLE Setta un nuovo stile per la finestra.
GWL_WNDPROC Setta un nuovo indirizzo per la procedura della finestra.
GWL_HINSTANCE Setta una nuova handle per l' istanza dell' applicazione.
GWL_ID Setta un nuovo identificatore per la finestra.
GWL_USERDATA Setta il valore a 32-bit associato alla finestra. Ogni finestra ha un corrispondente valore a 32-bit progettato ad uso dell'applicazione che ha creato la finestra.
dwNewLong = il valore sostitutivo.
Così il nostro lavoro è facile: Codifichiamo una proc per la finestra che maneggierà i messaggi per la casella di modifica ed allora chiameremo SetWindowLong con il flag GWL_WNDPROC , passaremo l'indirizzo della nostra proc per la finestra nel terzo parametro. Se la funzione riesce, il valore di ritorno è il precedente numero dell'intero a 32-bit specificato, nel nostro caso, l'indirizzo della procedura originale per la finestra. Dobbiamo memorizzare questo valore per usarlo all'interno della nostra procedura. Ricordartevi che ci saranno alcuni messaggi che non desideriamo maneggiare, li passeremo alla procedura originale per la finestra. Possiamo fare questo chiamando la funzione CallWindowProc.
lpPrevWndFunc:DWORD,
hWnd:DWORD,
Msg:DWORD,
wParam:DWORD,
lParam:DWORD
lpPrevWndFunc = l'indirizzo della procedura originale per la finestra.
I quattro parametri restanti sono quelli passati alla nostra procedura per la finestra. Li passeremo alla CallWindowProc.
.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
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SubclassWinClass",0
AppName db "Subclassing Demo",0
EditClass db "EDIT",0
Message db "You pressed Enter in the text box!",0
.data?
hInstance HINSTANCE ?
hwndEdit dd ?
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,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,350,200,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,WS_EX_CLIENTEDGE,ADDR EditClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
20,300,25,hWnd,NULL,\
hInstance,NULL
mov hwndEdit,eax
invoke SetFocus,eax
;-----------------------------------------
; Subclass it!
;-----------------------------------------
invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax
.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
.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
mov OldWndProc,eax
Dopo che la casella di modifica è stata creata, noi la subclassiamo chiamando SetWindowLong, sostituiamo l'indirizzo della procedura originale per la finestra con la nostra procedura per la finestra. Si noti che memorizziamo l'indirizzo della procedura originale per la finestra per usarlo con CallWindowProc. Notare EditWndProc è una procedura ordinaria per la finestra.
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
All'interno di EditWndProc, filtriamo i messaggi WM_CHAR. Se il carattere è fra 0-9 o a-f, lo accettiamo passando il messaggio alla procedura originale per la finestra. Se è un carattere minuscolo, lo convertiamo in maiuscolo aggiungendogli 20h. Notate che, se il carattere non è fra quelli che prevediamo, lo scartiamo. Non lo passiamo alla proc originale per la finestra. Così quando l'utente digita qualcosa tranne 0-9 o a-f, il carattere non compare nella casella di modifica.
mov eax,wParam
.if al==VK_RETURN
invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
invoke SetFocus,hEdit
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.end
Desidero dimostrare la potenza del subclassing ulteriormente intrappolando il tasto Enter. EditWndProc controlla se il messaggio WM_KEYDOWN è VK_RETURN (il tasto Enter). Se è così, visualizza una finestra di messaggio che dice che " avete premuto il tasto Enter nella casella di testo!". Se non è il tasto Enter, passa il messaggio alla procedura originale per la finestra. Potete usare il subclassing di una finestra per prendere il controllo su altre finestre. È una delle tecniche più potenti che dovreste avere nel vostro arsenale.
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.