Zoom Icon

Tutorial 4: Disegnare con il Testo

From UIC

Tutorial 4: Disegnare con il Testo

Contents


Infos
Author: Iczelion
Email: Traduttore: -NeuRaL_NoiSE
Website: Mirror
Date: 01/01/2001 (dd/mm/yyyy)
Level: Working brain required
Language: Italian Image:Flag_Italian.gif
Comments:



Introduzione

In questo tutorial, impareremo come "disegnare" del testo nella client area di una finestra. Impareremo anche cos'e' un device context (contesto di periferica, NdT).


Preliminari

Il testo in Windows e' un tipo di oggetto GUI. Ogni carattere e' composto da numerosi pixels che sono ammassai insieme in un pattern ben definito. Ecco perche' e' chiamato "painting" (disegnare, NdT) invece di "writing" (scrivere, NdT). Normalmente, disegnerete il testo nella vostra propria client area (in realta', potete disegnare anche al di fuori della client area ma quella e' un'altra storia). Mettere testo su schermo in Windows e' drasticamente diverso dal DOS. In DOS, potete considerare lo schermo di dimensioni 80x25. Ma, in Windows, lo schermo e' condiviso da diversi programmi. Bisogna seguire alcune regole ben precise per far si che dei programmi scrivano sulla parte dello schermo di altri programmi e cosi' via. Windows assicura che cio' non accada limitando la painting area (area in cui e' possibile scrivere/disegnare, NdT) di ogni finestra alla PROPRIA CLIENT AREA unicamente. Le dimensioni della client area di una finestra non sono costanti. L'utente puo' cambiare le dimensioni in qualsiasi momento. Pertanto, dovete determinare le dimensioni della client area dinamicamente, durante l'esecuzione. Prima di poter disegnare qualcosa nella client area, dovete chiedere il permesso a Windows. Esatto, non avete il controllo assoluto dello schermo come ai tempi del DOS. Dovete chiedere a Windows il permesso di disegnare nella vostra client area. Windows determinera' le dimensioni della client area, le font, i colori e altri attributi GDI, e mandera' un handle per il contesto di periferica (device context) in ritorno al vostro programma. Potete quindi utilizzare il device context come un passaporto per disegnare nella vostra client area. Cos'e' un device context? E' semplicemente un struttura di dati mantenuta internamente da Windows. Un device context e' associato con una determinata periferica (device, NdT), come ad esempio una stampante o lo schermo. Per lo schermo, solitamente un device context e' associato con una particolare finestra presente su di esso. Alcuni dei valori nel device context sono attributi grafici come colore, tipo di font, ecc. Questi sono valori predefiniti che potete cambiare a piacimento. Essi esistono per aiutare a ridurre il fastidio di dover specificare tali attributi in ogni chiamata a funzioni GDI. Quando un programma deve disegnare, esso deve ottenere l'handle di un device context. Normalmente, cio' si puo' fare in due modi. • chiamare BeginPaint in risposta al messaggio WM_PAINT. • chiamare GetDC in risposta ad altri messaggi. Una cosa che dovete ricordare, una volta completate le operazioni relative all'handle del device context, e' che dovete rilasciarlo durante l'elaborazione di un singolo messaggio. Non commettete l'errore di trovare l'handle in risposta ad un messaggio e poi di rilasciarlo in riposta ad un altro. Windows manda i messaggi WM_PAINT ad una finestra per notificare che e' ora di ridisegnare la propria client area. Windows non salva il contenuto della client area di una finestra. Invece, quando si verifica una situazione che garantisce il ridipingimento di una client area (come ad esempio quando una finestra che era stata coperta da un'altra viene rimessa in primo piano), Windows aggiunge il messaggio WM_PAINT nella coda dei messaggi di quella finestra. E' poi responsabilita' di quella finestra il ridipingimento della propria client area. Voi dovete inserire tutte le informazioni inerenti il ridipingimento della vostra client area nella sezione relativa a WM_PAINT della vostra window procedure, cosicche' la la window procedure puo' ridisegnare la client area quando sopraggiunge il messaggio WM_PAINT. Un altro concetto con cui dovrete avere a che fare e' l'invalid rectangle (rettangolo invalido, NdT). Windows definisce un rettangolo invalido come la piu' piccola area rettangolare nella client area che abbia bisogno di essere ridisegnata. Quando Windows rileva un rettangolo invalido nella client area di una finestra, manda il messaggio WM_PAINT a quella finestra. In risposta al messaggio WM_PAINT, la finestra puo' ritrovare una struttura paintstruct che contiene, tra l'altro, le coordinate del rettangolo invalido. Voi chiamate BeginPaint in risposta al messaggio WM_PAINT per validare il rettangolo invalido. Se non elaborate il messaggio WM_PAINT, quanto meno dovete chiamare DefWindowProc o ValidateRect per validare il rettangolo invalido, altrimenti Windows continuera' all'infinito a mandarvi il messaggio WM_PAINT. Ecco i passi che dovete seguire in risposta ad un messaggio WM_PAINT: • Ottenere l'handle del device context con BeginPaint. • Disegnare la client area. • Rilasciare l'handle del device context con EndPaint Notate che non dovete esplicitamente validare il rettangolo invalido. Cio' e' fatto automaticamente dalla chiamata a BeginPaint. Nelle istruzioni tra BeginPaint ed EndPaint, potete chiamare qualsiasi funzione GDI per disegnare la vostra client area. Praticamente tutte richiedono un handle di un device context come uno dei parametri.

Contenuto

Scriveremo un programma che mostra la stringa testuale "Win32 assembly is great and easy!" al centro della client area.

include windows.inc
includelib user32.lib
includelib kernel32.lib
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.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_WINDOW+1
mov wc.lpszMenuName,NULL
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,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,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
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
mov eax,uMsg
.IF eax==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF eax==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start

La maggior parte del codice e' uguale a quello nell'esempio del tutorial 3. Spieghero' soltanto i cambiamenti importanti.

   LOCAL hdc:HDC  
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT

Queste sono variabili locali che vengono usate dalle funzioni GDI nella nostra sezione WM_PAINT. hdc e' usata per conservare l'handle del device context restituito dalla chiamata a BeginPaint. ps e' una struttura PAINTSTRUCT. Normalmente non utilizzate i valori in ps. E' passata alla funzione BeginPaint e Windows la riempie con i valori appropriati. Successivamente, passate ps alla funzione EndPaint quando avete finito di ridisegnare la client area. rect e' una struttura RECT definita come segue:

RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends

Left e top sono le coordinate dell'angolo superiore sinistro di un rettangolo. Right e bottom sono le coordinate dell'angolo inferiore destro. Una cosa da ricordare: L'origine delle assi x-y e' nell'angolo superiore sinistro della client area. Pertanto il punto y=10 e' AL DI SOTTO del punto y=0.

invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps

In risposta al messaggio WM_PAINT, voi chiamate BeginPaint con l'handle della finestra in cui volete disegnare ed una struttura PAINTSTRUCT non inizializzata come parametri. Dopo che la call e' stata eseguita con successo, eax contiene l'handle del device context. Successivamente, chiamate GetClientRect per trovare la dimensione della client area. La dimensione viene restituita nella variabile rect che voi passate a DrawText come uno dei suoi parametri. La sintassi di DrawText e':

int WINAPI DrawText(HDC hdc,
LPCSTR lpString,
int nCount,
LPRECT lpRect,
UNIT uFormat);

DrawText e' una funzione API di output ad alto livello. Essa gestisce alcuni dettagli come il word wrap (a capo automatico, NdT), testo centrato, ecc. percio' vi potete concentrare sulla stringa che volete disegnare. Il suo fratello a basso livello, TextOut, sara' esaminato nel prossimo tutorial. DrawText formatta una stringa di testo per far si che essa rientri nei limiti di un rettangolo. Essa usa la font, il colore e lo sfondo (nel device context) correntemente impostati per disegnare il testo. Le linee sono automaticamente interrotte e riprese al nuovo rigo per rientrare nei limiti del rettangolo. La funzione restituisce l'altezza del testo di output in unita' relative alla periferica, nel nostro caso pixels. Diamo un'occhiata ai suoi parametri:
hdc handle del device context
lpString Un puntatore alla stringa che volete disegnare nel rettangolo. La stringa deve essere null-terminated (terminata con zero, NdT) o altrimenti dovreste specificare la sua lunghezza nel parametro successivo, nCount.
nCount Il numero di caratteri da scrivere. Se la stringa e' null-terminated, nCount deve essere -1. Altrimenti nCount deve contenere il numero dei caratteri nella stringa che voi volete disegnare.
lpRect Un puntatore ad un rettangolo (una struttura di tipo RECT) all'interno del quale volete disegnare la stringa. Notate che questo rettangolo e' anche un clipping rectangle, che significa che non potete disegnare la stringa al di fuori di questo rettangolo.
uFormat Il valore che specifica in che modo la stringa viene mostrata nel rettangolo. Noi utilizziamo tre valori combinati dall'operatore "or":

  • DT_SINGLELINE specifica una singola linea di testo.
  • DT_CENTER centra il testo orizzontalmente.
  • DT_VCENTER centra il testo verticalmente. Deve essere usato con DT_SINGLELINE.

Quando avete finito di disegnare la client area, dovete chiamare la funzione EndPaint per rilasciare l'handle del device context. Ecco tutto. Possiamo sintetizzare i punti salienti in questo modo:

  • Chiamate la coppia BeginPaint-EndPaint in risposta al messaggio WM_PAINT.
  • Fate cio' che volete con la client area tra le chiamate a BeginPaint e ad EndPaint.
  • Se volete ridisegnare la vostra client area in risposta ad altri messaggi, avete due scelte:
  • Usare la coppia GetDC-ReleaseDC e eseguire le vostre operazioni di disegno tra queste chiamate.
  • Chiamare InvalidateRect o UpdateWindow per invalidare l'intera client area, forzando Windows ad inserire il messaggio WM_PAINT nella coda dei messaggi della vostra finestra, ed eseguire le vostre operazioni di disegno nella sezione relativa a WM_PAINT.


Note finali

Questi tutorials erano presenti nel sito di RingZero. Li rimettiamo a disposizione a chiunque voglia poter leggerli nella loro traduzione in italiano che fu curata da NeuralNoise.

phobos


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