Zoom Icon

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: Slightly hard
Language: Italian Flag Italian.gif
Comments: Formattazione Wiki: Antelox


Introduzione

In questo tutorial, impareremo circa il subclassing di finestra, che cosa è e come usarlo a nostro vantaggio.


Tools

Esempio


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.

SetWindowLong PROTO hWnd:DWORD, nIndex:DWORD, dwNewLong:DWORD

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.


CallWindowProc PROTO
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.

.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
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

invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
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.

.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

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.

.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
        .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.