Iczelion’s tutorial N°26 |
||
Data |
by "Death_Reaver" |
|
01/06/2005 |
Published by Quequero |
|
Sei solo in una stanza… |
Grazie Mille! |
…conti i coglioni e sono tre… |
.... |
Home page (se presente):NADA E-mail: [email protected] Nick, UIN, canale IRC/EFnet frequentato |
.... |
Difficoltà |
( )NewBies (*)Intermedio ( )Avanzato ( )Master |
|
Introduzione |
Continua la traduzione dei tutorial di Iczelion…MADE BY DEATH_REAVER
Tools usati |
ALLEGATO (codice sorgente + eseguibile già compilato)
MASM32 v.8 sp1 (nella sezione tools)
RadAsm (IDE perfetto per programmare in asm)
Notizie sul programma |
In questo tutorial impareremo a mostrare una splash-screen durante l’avvio di un nostro programma
Essay |
TEORIA
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
Lo schema generale che seguiremo sarà questo:
Esamineremo i punti in dettaglio.
Caricamento/Scaricamento DLL
Puoi caricare dinamicamente una DLL con LoadLibrary che ha la seguente sintassi:
LoadLibrary PROTO lpDLLName:DWORD
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
FreeLibrary proto hLib:DWORD
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
SetTimer proto hWnd:DWORD,TimerID:DWORD, uElapse:DWORD, lpTimerFunc:DWORD
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:
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
KillTimer proto hWnd:DWORD,TimerID:DWORD
Esempio:
;----------------------------------------------------------------------- ; Il programma principale ;----------------------------------------------------------------------- .386 (vi ricordo che in allegato c’è il mio include file {Death_Reaver.inc} che .model flat,stdcall vi fa risparmiare di scrivere le intestazioni, include e roba varia) 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 ; When the dll is loaded 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.
invoke LoadLibrary,addr Libname .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.
.if reason==DLL_PROCESS_ATTACH ; quando il processo è caricato 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:
INVOKE CreateWindowEx,NULL,ADDR ClassName,NULL,\ 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 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
|
Restituisce le dimenzioni del desktop e la finestra quindi calcola le giuste coordinate dall'angolo superiore sinistro per mettersi al centro.
invoke LoadBitmap,hInstance,addr BitmapName 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.
.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 |
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.
.elseif uMsg==WM_LBUTTONDOWN 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.
.elseif uMsg==WM_TIMER 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.
DEATH_REAVER
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 |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo che ogni sviluppatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Reversiamo al solo scopo informativo e per migliorare la nostra conoscenza del linguaggio Assembly.