Iczelion 26
From UIC
Tutorial 26: Splash Screen
Contents |
| Iczelion 26 | |
|---|---|
| Author: | Iczelion |
| Email: | Translate: Death_Reaver |
| Website: | Mirror |
| Date: | 06/03/2009 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
Ora che sappiamo come usare una bitmap, siamo in grado di fare qualcosa di più avanzato con essa.
Tools
Essay
Una splash-screen è una finestra che non ha la barra del titolo, il system menu, bordi che mostra soltanto una bitmap per un pò e poi sparisce automaticamente. Di solito è usata durante l'avvio di un programma per mostrare il logo di tale programma o distrarre l'attenzione dell'utente mentre il programma ha lunghe fasi di inizializzazione. In questo tutorial implementeremo una splash-screen. Il primo passo è di includere la bitmap nel file di risorse. Se ci pensi però, è una grande perdita di memoria caricare la bitmap che useremo solo una volta e tenerla in memoria fino alla chiusura del programma. Una soluzione ottimale sarebbe di creare una "DLL di risorse" che contenga la bitmap e che abbia il solo compito di mostrare lo splash-screen. In questo modo puoi caricare la DLL quando vuoi mostrare la splash-screen e scaricarla quando non serve più. Perciò avremo due moduli: il programma principale e la "splash-DLL". Metteremo la bitmap nelle risorse della DLL.
Lo schema generale che seguiremo sarà questo:
- Mettere la bitmap nella DLL come risorsa
- Il programma principale chiamerà LoadLibrary per caricare la DLL.
- La funzione di entry-point della DLL sarà chiamata. Creerà un timer settando il tempo in cui la splash-screen resterà visibile. Dopo registreremo e creeremo una finestra senza titolo e bordi e infine metteremo la bitmap nella client-area.
- Quando il tempo specificato sopra sarà passato, la splash-screen sarà rimossa dallo schermo e il controllo tornerà al programma principale.
- Il programma principale chiamerà FreeLibrary per scaricare la DLL dalla memoria e quindi farà i compiti che deve.
Esamineremo i punti in dettaglio.
Caricamento/Scaricamento DLL
Puoi caricare dinamicamente una DLL con LoadLibrary che ha la seguente sintassi:
Prende solo un parametro: l'indirizzo al quale è contenuto il nome della DLL da caricare in memoria. Se la chiamata ha successo, ritorna con l'handle della DLL altrimenti ritorna con NULL. Per scaricare la DLL si chiama FreeLibrary
Prende solo un parametro: l'handle della DLL da scaricare. Normalmente l'handle lo prendi da LoadLibrary.
Come usare un Timer
Per prima cosa devi crearlo con SetTimer
hWnd è l'handle della finestra che riceverà i messaggi di notifica. Questo parametro può essere NULL per specificare che non c'è nessuna finestra associata al timer;
TimerID è un valore definito dell'utente che è usata come ID del timer;
uElapse è l‘intervallo (ogni quando manda i messaggi di notifica, in millisecondi);
lpTimerFunc è l'indirizzo di una funzione che processerà il messaggio di notifica del timer. Se è settato a NULL i messaggi del timer saranno mandati alla finestra specificata da hWnd (WM_TIMER);
SetTimer, se ha successo, restituisce l'ID di un timer. Altrimenti restituisce NULL. Quindi è meglio non associare a una TimerID zero
Puoi creare un timer in due modi:
- Se hai una finestra e vuoi che i messaggio di notifica del timer vadano alla tua finestra devi passare tutti i parametri a SetTimer e inoltre lpTimerFunc deve essere NULL.
- Se non hai una finestra o non vuoi processare i messaggi del timer nella WndProc, devi passare NULL al valore di hWnd. Devi inoltre specificare l'indirizzo della timer-function che processerà i messaggi. Useremo il primo approccio nell'esempio.
Quando il periodo di intervallo scade, un WM_TIMER è mandato alla finestra associata al timer. Per esempio se specifichi uElapse a 1000, la tua finestra riceverà un WM_TIMER ogni secondo.
Quando non hai più bisogno di un timer, va distrutto con KillTimer
Esempio
; Il programma principale
;--------------------------------------------------------------------------------------------------------
.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
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SplashDemoWinClass",0
AppName db "Splash Screen Example",0
Libname db "splash.dll",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
invoke LoadLibrary,addr Libname
.if eax!=NULL
invoke FreeLibrary,eax
.endif
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, 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 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,eax
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
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
;--------------------------------------------------------------------------------------------------------
; La Bitmap DLL
;--------------------------------------------------------------------------------------------------------
.386
.model flat, stdcall
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data
BitmapName db "MySplashBMP",0
ClassName db "SplashWndClass",0
hBitMap dd 0
TimerID dd 0
.data
hInstance dd ?
.code
DllEntry proc hInst:DWORD, reason:DWORD, reserved1:DWORD
.if reason==DLL_PROCESS_ATTACH ; quando il processo è caricato
push hInst
pop hInstance
call ShowBitMap
.endif
mov eax,TRUE
ret
DllEntry Endp
ShowBitMap proc
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,NULL,
WS_POPUP,CW_USEDEFAULT,
CW_USEDEFAULT,250,250,NULL,NULL,
hInstance,NULL
mov hwnd,eax
INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
.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
ShowBitMap endp
WndProc proc hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL ps:PAINTSTRUCT
LOCAL hdc:HDC
LOCAL hMemoryDC:HDC
LOCAL hOldBmp:DWORD
LOCAL bitmap:BITMAP
LOCAL DlgHeight:DWORD
LOCAL DlgWidth:DWORD
LOCAL DlgRect:RECT
LOCAL DesktopRect:RECT
.if uMsg==WM_DESTROY
.if hBitMap!=0
invoke DeleteObject,hBitMap
.endif
invoke PostQuitMessage,NULL
.elseif uMsg==WM_CREATE
invoke GetWindowRect,hWnd,addr DlgRect
invoke GetDesktopWindow
mov ecx,eax
invoke GetWindowRect,ecx,addr DesktopRect
push 0
mov eax,DlgRect.bottom
sub eax,DlgRect.top
mov DlgHeight,eax
push eax
mov eax,DlgRect.right
sub eax,DlgRect.left
mov DlgWidth,eax
push eax
mov eax,DesktopRect.bottom
sub eax,DlgHeight
shr eax,1
push eax
mov eax,DesktopRect.right
sub eax,DlgWidth
shr eax,1
push eax
push hWnd
call MoveWindow
invoke LoadBitmap,hInstance,addr BitmapName
mov hBitMap,eax
invoke SetTimer,hWnd,1,2000,NULL
mov TimerID,eax
.elseif uMsg==WM_TIMER
invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL
invoke KillTimer,hWnd,TimerID
.elseif uMsg==WM_PAINT
invoke BeginPaint,hWnd,addr ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemoryDC,eax
invoke SelectObject,eax,hBitMap
mov hOldBmp,eax
invoke GetObject,hBitMap,sizeof BITMAP,addr bitmap
invoke StretchBlt,hdc,0,0,250,250,
hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY
invoke SelectObject,hMemoryDC,hOldBmp
invoke DeleteDC,hMemoryDC
invoke EndPaint,hWnd,addr ps
.elseif uMsg==WM_LBUTTONDOWN
invoke DestroyWindow,hWnd
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
End DllEntry
Analisi
Esamineremo il codice del programma per primo.
.if eax!=NULL
invoke FreeLibrary,eax
.endif
Chiamiamo LoadLibrary per caricare la DLL chiamata "splash.dll". Dopodichè la scarichiamo con FreeLibrary. LoadLibrary ritornerà solo quando la DLL ha finito la sua inizializzazione. E' tutto quello che fà il programma. Il bello è nella DLL.
push hInst
pop hInstance
call ShowBitMap
quando la DLL è caricata, Windows chiama la sua funzione di entry-point con il flag DLL_PROCESS_ATTACH. Usiamo questa opportunità per mostrare la splash-screen.
Per prima cosa, mettiamo al sicuro l'handle della DLL per uso futuro. Quindi chiamiamo una funzione (ShowBitMap) la quale fa il vero lavoro. ShowBitMap registra una window-class(classe di finestra), crea una finestra ed entra in message-loop come al solito. La parte interessante è nella chiamata a CreateWindowsEx:
WS_POPUP,CW_USEDEFAULT,
CW_USEDEFAULT,250,250,NULL,NULL,
hInstance,NULL
Notare che lo stile finestra è solo WS_POPUP che creerà una finestra senza bordi e senza titolo. Inoltre limitiamo l'altezza e la larghezza della finestra a 250x250 pixel.
Quando la finestra è creata, in WM_CREATE moviamo la finestra al centro dello schermo con il seguente codice:
invoke GetDesktopWindow
mov ecx,eax
invoke GetWindowRect,ecx,addr DesktopRect
push 0
mov eax,DlgRect.bottom
sub eax,DlgRect.top
mov DlgHeight,eax
push eax
mov eax,DlgRect.right
sub eax,DlgRect.left
mov DlgWidth,eax
push eax
mov eax,DesktopRect.bottom
sub eax,DlgHeight
shr eax,1
push eax
mov eax,DesktopRect.right
sub eax,DlgWidth
shr eax,1
push eax
push hWnd
call MoveWindow
Restituisce le dimenzioni del desktop e la finestra quindi calcola le giuste coordinate dall'angolo superiore sinistro per mettersi al centro.
mov hBitMap,eax
invoke SetTimer,hWnd,1,2000,NULL
mov TimerID,eax
Poi carica la bitmap dal file di risorse con LoadBitmap e crea un timer con ID == 1 e l'intervallo di 2 secondi (2000 mmsec). Il timer manderà WM_TIMER alla finestra ogni 2 secondi.
invoke BeginPaint,hWnd,addr ps
mov hdc,eax
invoke CreateCompatibleDC,hdc
mov hMemoryDC,eax
invoke SelectObject,eax,hBitMap
mov hOldBmp,eax
invoke GetObject,hBitMap,sizeof BITMAP,addr bitmap
invoke StretchBlt,hdc,0,0,250,250,
hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY
invoke SelectObject,hMemoryDC,hOldBmp
invoke DeleteDC,hMemoryDC
invoke EndPaint,hWnd,addr ps
Quando la finestra riceve un WM_PAINT, crea un DC, un mDC (device context in memoria), ci inserisce la bitmap, ne ottiene la grandezza con GetObject e infine mette la bitmap nella finestra con StretchBlt che funziona come BitBlt ma può allargare o stringere la bitmap per raggiungere la dimensioni volute. In questo caso vogliamo che la bitmap riempia la finestra percui usiamo StretchBlt invece che BitBlt. Dopo questa operazione cancelliamo il DC.
invoke DestroyWindow,hWnd
Sarebbe frustrante per l'utente aspettare la scomparsa della splash-screen. Possiamo risolvere questo problema con una scelta: quando clicka sulla splash-screen, questa sparisce. E' l'unico motivo per il quale processiamo WM_LBUTTONDOWN nella DLL. Quando riceve tale messaggio, la finestra viene distrutta chiamando DestroyWindow.
invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL
invoke KillTimer,hWnd,TimerID
Se l'utente sceglie di aspettare, la splash-screen sparirà quando il timer avrà raggiunto l'intervallo specificato (2 secondi). Possiamo farlo processando il messaggio WM_TIMER. Quando si riceviamo tale messaggio chiudiamo la finestra mandando un WM_LBUTTONDOWN alla finestra (con SendMessage). In questo modo ci risparmiamo di scrivere due volte il codice. Non abbiamo più bisogno del timer, percui lo distruggiamo con KillTimer. Quando la finestra è chiusa, la DLL ridarà il controllo al programma principale.
Note Finali
Spero che la traduzione sia stata chiara. Per eventuali chiarimenti o altro, contattatemi (l'indirizzo e-mail è sopra). Un saluto a TUTTI!!!!!!!
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.