Tutorial 9: I Controlli di Child Window
From UIC
Tutorial 9: I Controlli di Child Window
Contents |
| Tutorial 9: I Controlli di Child Window | |
|---|---|
| Author: | Iczelion |
| Email: | Traduttore: -NeuRaL_NoiSE |
| Website: | Mirror |
| Date: | 30/12/2008 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
In questo tutorial, esploreremo i controlli di Child Window, che sono importanti periferiche di input ed output dei nostri programmi.
Tools
Preliminari
Windows fornisce diverse window classes predefinite che possiamo usare istantaneamente nei nostri programmi. La maggior parte delle volte le usiamo come componenti di una dialog box, percio' vengono chiamate solitamente "controlli di child window". I controlli di child window elaborano i loro propri messaggi dal mouse e dalla tastiera e notificano la parent window (finestra a cui appartengono, NdT) quando il loro stato e' cambiato. Essi tolgono un gran peso dalle spalle dei programmatori, percio' dovreste usarli quanto piu' possibile. In questo tutorial, li inseriro' in una finestra normale semplicemente per dimostrarvi come potete crearli ed usarli, ma solitamente dovreste utilizzarli in una dialog box. Esempi di window classes predefinite sono il button, la listbox, la checkbox, il radio button, l'edit ecc. Per poter usare un controllo di child window, dovete crearlo con CreateWindow o CreateWindowEx. Notate che non dovrete registrare la window class, poiche' essa e' registrata per voi da Windows. Il parametro class name DEVE essere il class name predefinito. Ad esempio, se volete creare un button, dovete specificare "button" come class name in CreateWindowEx. Gli altri parametri che dovete riempire sono l'handle della parent window e la control ID. La control ID deve essere unica tra i controlli. La control ID e' l'ID di quel controllo. La usate per distinguere i controlli. Una volta creato il controllo, questo mandera' dei messaggi per notificare alla parent window quando il suo stato e' cambiato. Normalmente, potete creare le child windows nella sezione relativa al messaggio WM_CREATE della parent window. La child window manda messaggi WM_COMMAND alla parent window con la sua control ID nella low word di wParam, il codice di notifica nella high word di wParam, e il suo window handle in lParam. Ogni controllo di child window ha un diverso codice di notifica (notification codes) rispetto agli altri, riferitevi alla vostra Win32 API reference per maggiori informazioni. Anche la parent window puo' mandare comandi alle child windows, chiamando la funzione SendMessage. La funzione SendMessage manda il messaggio specificato con i relativi valori in wParam e lParam alla finestra specificata dal window handle. E' una funzione estremamente utile poiche' puo' mandare messaggi a qualsiasi finestra, posto che voi conosciate il suo window handle. Percio', una volta create le child windows, la parent window deve elaborare i messaggi WM_COMMAND per essere in grado di ricevere codici di notifica dalle child windows.
Essay
Creeremo una finestra che contiene un edit control ed un pushbutton. Quando clickate sul button, una message box apparira', mostrandovi il testo che avevate scritto nell'edit box. C'e' anche un menu con 4 menu items:
- Say Hello -- Scrive una stringa di testo nell'edit box
- Clear Edit Box -- Cancella il contenuto dell'edit box
- Get Text -- Mostra una message box con il testo nell'edit box
- Exit -- Chiude il programma.
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0
ButtonClassName db "button",0
ButtonText db "My First Button",0
EditClassName db "edit",0
TestString db "Wow! I'm in an edit box now",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndButton HWND ?
hwndEdit HWND ?
buffer db 512 dup(?),0 ; buffer per salvare il testo catturato dall'edit box
.const
ButtonID equ 1 ; La control ID del button control
EditID equ 2 ; La control ID dell'edit control
IDM_HELLO equ 1
IDM_CLEAR equ 2
IDM_GETTEXT equ 3
IDM_EXIT equ 4
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
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_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
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_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT,\
300,200,NULL,NULL, hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg ; Traduce l'input "grezzo" da tastiera in input di un carattere
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_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF eax==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50,35,200,25,hWnd,8,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or ES_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
.ELSEIF eax==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analisi
Analizziamo il programma.
invoke CreateWindowEx,WS_EX_CLIENTEDGE, \
ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT\
or ES_AUTOHSCROLL,\
50,35,200,25,hWnd,EditID,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,\
ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or ES_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
Creiamo i controlli durante l'elaborazione del messaggio WM_CREATE. Chiamiamo CreateWindowEx con un window style extra, WS_EX_CLIENTEDGE, che fa sembrare la client area infossata. Il nome di ogni controllo e' un nome predefinito, "edit" per l'edit control, "button" per il button control. Successivamente specifichiamo gli stili della child window. Ciascun controllo ha stili extra in aggiunta ai normali stili della finestra. Ad esempio, gli stili relativi ai buttons hanno come prefisso "BS_", che significa "button style", mentre gli stili relativi all'edit hanno come prefisso "ES_" che significa "edit style". Dovete cercare questi stili nella Win32 API reference. Notate che potete mettere una control ID al posto del menu handle. Questo non causa nessun problema poiche' un controllo di una child window non puo' avere un menu. Dopo aver i creato controlli, conserviamo gli handle di ciascuno di essi in delle variabili per un uso futuro. SetFocus viene chiamata per dare l'input focus all'edit box cosicche' l'utente puo' scrivere il testo dentro di esso immediatamente. Adesso arriva la parte davvero interessante. Ogni controllo di child window notifica gli eventi alla propria parent window con WM_COMMAND.
mov eax,wParam
.IF lParam==0
Tenete presente che anche i menu mandano messaggi WM_COMMAND per notificare la finestra circa il loro stato. Come si puo' distinguere un messaggo WM_COMMAND originato da un menu e uno originato da un controllo? Qui sotto c'e' la risposta.
| Low word di wParam | High word di wParam | lParam | |
|---|---|---|---|
| Menu | Menu ID | 0 | 0 |
| Control | Control ID | Codice di notifica | Handle della Child Window |
Potete osservare che e' necessario controllare lParam. Se e' zero, il messaggo WM_COMMAND elaborato proviene da un menu. Potete utilizzare wParam per distinguere tra un menu ed un controllo dal momento che la menu ID e la control ID potrebbero essere uguali ed il codice di notifica potrebbe essere zero.
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
Potete scrivere una stringa di testo in un edit box chiamando SetWindowText. Potete cancellare il contenuto di un edit box chiamando SetWindowText con NULL. SetWindowText e' una funzione API utile per piu' scopi. Potete usare SetWindowText anche per cambiare il titolo di una finestra o la scritta su un bottone.
Per prendere il testo da un edit box, userete GetWindowText.
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
Queste istruzioni hanno a che fare con la condizione in cui ci si trova quando l'utente preme il bottone. Innanzitutto, la routine controlla la low word di wParam per vedere se la control ID coincide con quella del button. Se si, controlla la high word di wParam per vedere se c'e' il codice di notifica BN_CLICKED che viene inviato quando un button e' stato premuto. La parte interessante viene dopo che ci si e' assicurati che il codice di notifica e' BN_CLICKED. Vogliamo prendere il testo scritto nell'edit box e mostrarlo in una message box. Potremmo duplicare il codice nella sezione IDM_GETTEXT di cui sopra ma questo sarebbe piuttosto scomodo. Se in qualche modo potessimo mandare un messaggio WM_COMMAND con la low word di wParam contenente il valore IDM_GETTEXT alla nostra window procedure, potremmo evitare la duplicazione del codice e semplificheremmo il nostro programma. La funzione SendMessage e' la soluzione. Questa funzione manda qualsiasi messaggio a qualsiasi finestra con qualsiasi wParam e lParam a nostra scelta. Percio', invece di duplicare le istruzioni, chiamiamo SendMessage con l'handle della parent window, WM_COMMAND, IDM_GETTEXT, e 0. Questo procedimento ottiene lo stesso effetto di quando si seleziona il menu item "Get Text" dal menu. La window procedure non rileva alcuna differenza tra i due. Dovreste usare questa tecnica quanto piu' spesso possibile per rendere il vostro codice meglio strutturato. Infine, non dimenticatevi della funzione TranslateMessage nel message loop. Visto che dovete digitare del testo nell'edit box, il vostro programma deve tradurre l'input "grezzo" (raw) dalla tastiera in testo leggibile. Se omettete questa funzione, non potrete scrivere niente nel vostro edit box.
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.