Iczelion’s tutorial N°26
Splash-Screen

Data

by "Death_Reaver"

 

01/06/2005

UIC's Home Page

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

 

  1. Mettere la bitmap nella DLL come risorsa
  2. Il programma principale chiamerà LoadLibrary per caricare la DLL.
  3. 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.
  4. Quando il tempo specificato sopra sarà passato, la splash-screen sarà rimossa dallo schermo e il controllo tornerà al programma principale.
  5. Il programma principale chiamerà FreeLibrary per scaricare la DLL dalla memoria e quindi farà i compiti che deve.
  6.  

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