Zoom Icon

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

Esempio


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

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

.if uMsg==WM_CREATE
         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.

push wc.lpfnWndProc
        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.

invoke RegisterClassEx, addr wc

Quando tutto è pronto, registrate la nuova classe. Otterrete una nuova classe con alcune caratteristiche della vecchia classe.

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

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.

.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

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.