![]() |
Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr Jerzy Wałaszek I LO w Tarnowie
|
Teraz, gdy już poznaliśmy sposoby wykorzystania grafiki rastrowej, możemy przejść dalej - do bardziej twórczego jej zastosowania. Oto ekran startowy.

Załaduj {ten przykład}
Ekran startowy (splash screen) jest oknem, które nie posiada paska tytułowego, przycisków systemowych, brzegu i wyświetla grafikę rastrową przez pewien czas, a później znika automatycznie. Zwykle stosuje się go podczas startu programu do wyświetlenia logo lub do odwrócenia uwagi użytkownika, gdy program dokonuje przydługawej inicjalizacji. Na tej lekcji zaimplementujemy w tworzonym programie ekran startowy.
Pierwszym krokiem jest dołączenie grafiki rastrowej do pliku zasobów. Jednakże, gdy przemyślisz to, okaże się, iż marnujemy cenną pamięć do załadowania grafiki używanej w programie tylko jeden raz i przechowywanie jej aż do jego zakończenia. Lepszym rozwiązaniem jest utworzenie biblioteki DLL "zasobów", która zawiera grafikę rastrową i jest przeznaczona tylko do wyświetlenia ekranu startowego. W ten sposób możesz załadować bibliotekę DLL, gdy chcesz wyświetlić ekran startowy i usunąć ją, gdy przestanie już być potrzebna. Zatem będziemy posiadali dwa moduły: program główny i bibliotekę DLL ekranu startowego. Grafikę rastrową umieścimy w zasobach biblioteki DLL.
Ogólny schemat jest następujący:
Szczegółowo zbadamy te mechanizmy.
Przy pomocy funkcji LoadLibrary można dynamicznie załadować bibliotekę DLL. Funkcja ta ma następującą składnię:
LoadLibrary PROTO lpDLLName:DWORD
Przyjmuje ona tylko jeden parametr: adres nazwy biblioteki DLL, którą chcemy załadować do pamięci. Jeśli wywołanie to się powiedzie, zwróci uchwyt modułu biblioteki DLL, w przeciwnym wypadku zwracana jest wartość NULL.
Aby usunąć bibliotekę DLL, wywołujemy FreeLibrary:
FreeLibrary PROTO hLib:DWORD
Przyjmuje ona jeden parametr: uchwyt modułu biblioteki DLL, którą chcemy usunąć. Zwykle uchwyt ten otrzymujesz jako rezultat wywołania funkcji LoadLibrary
Najpierw musisz stworzyć licznik czasu (timer) za pomocą funkcji SetTimer:
SetTimer PROTO hWnd: DWORD,\
TimerID: DWORD,\
uElapse: DWORD,\
lpTimerFunc: DWORDJeśli wywołanie funkcji SetTimer się powiedzie, to zwróci ona numer ID utworzonego licznika czasu. W przeciwnym wypadku zwróci NULL. Zatem nie powinieneś na numer licznika czasu używać liczby 0. Licznik czasu można utworzyć na dwa sposoby:
W naszym przykładzie użyjemy pierwszego rozwiązania.
Gdy minie zadany okres czasu, zostaje wysłana wiadomość WM_TIMER do okna powiązanego z licznikiem czasu. Na przykład, jeśli określisz uElapse na 1000, twoje okno będzie otrzymywało co każdą sekundę wiadomość WM_TIMER.
Gdy licznik czasu przestanie ci już być potrzebny, usuń go za pomocą funkcji KillTimer:
KillTimer PROTO hWnd:DWORD, TimerID:DWORD
Plik SPLASH.ASM
.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 "Ekran startowy", 0
Libname DB "splashdll.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
Plik SPLASHDLL.ASM
.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 ;Gdy DLL jest ładowana
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, 320, 400, 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 ;Część późniejszego wywołania MoveWindow
mov eax, DlgRect.bottom ;Pobieramy spód okna dialogowego
sub eax, DlgRect.top ;Obliczamy wysokość okna
mov DlgHeight, eax ;Wynik zapamiętujemy w zmiennej
push eax ;Na stos dla wywołania MoveWindow
mov eax, DlgRect.right ;Prawy bok naszego okna dialogowego
sub eax, DlgRect.left ;Obliczamy szerokość okna
mov DlgWidth, eax ;Zapamiętujemy wynik
push eax ;Na stos dla wywołania MoveWindow
mov eax, DesktopRect.bottom ;Spód okna pulpitu
sub eax, DlgHeight ;Odejmujemy wysokość naszego okna
shr eax, 1 ;Dzielimy przez 2, co daje środek ekranu
push eax ;Na stos dla MveWindow
mov eax, DesktopRect.right ;Pobieramy prawy bok okna pulpitu
sub eax, DlgWidth ;Odejmujemy szerokość okna
shr eax, 1 ;Dzielimy wynik przez 2
push eax ;Na stos dla MoveWindow
push hWnd ;Na stos uchwyt okna
call MoveWindow ;Przesuwamy okno
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 BitBlt, hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,\
hMemoryDC, 0, 0, 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
Plik SPLASHDLL.DEF
LIBRARY SPLASHDLL.DLL
Plik SPLASHDLL.RC
MySplashBMP BITMAP "JOURNEY.BMP"
Najpierw zbadajmy kod w głównym programie.
start:
INVOKE LoadLibrary, ADDR Libname
.IF eax!=NULL
INVOKE FreeLibrary, eax
.ENDIF
Wywołujemy funkcję LoadLibrary do załadowania biblioteki DLL o nazwie splashdll.dll. A po tym fakcie usuwamy bibliotekę z pamięci przy pomocy FreeLibrary. Funkcja LoadLibrary nie powróci, aż biblioteka DLL zakończy swoją inicjalizację.
To wszystko co robi program główny. Interesujący fragment znajdziemy w bibliotece DLL.
DllEntry PROC hInst:DWORD, reason:DWORD, reserved1:DWORD
.IF reason==DLL_PROCESS_ATTACH ;Gdy DLL jest ładowana
push hInst
pop hInstance
call ShowBitMap
.ENDIF
mov eax, TRUE
ret
DllEntry ENDP
Gdy biblioteka DLL zostaje załadowana, system Windows wywołuje jej funkcję wejściową ze znacznikiem DLL_PROCESS_ATTACH. Korzystamy z tej okazji do wyświetlenia ekranu startowego. Najpierw zachowujemy do późniejszego wykorzystania uchwyt egzemplarza biblioteki DLL. Następnie wywołujemy funkcje o nazwie ShowBitMap do przeprowadzenia właściwej operacji. Funkcja ShowBitMap rejestruje klasę okna, tworzy okno i wchodzi jak zwykle do pętli wiadomości. Oto interesujący fragment w wywołaniu CreateWindowEx:
INVOKE CreateWindowEx, NULL, ADDR ClassName, NULL,\
WS_POPUP, CW_USEDEFAULT,\
CW_USEDEFAULT, 320, 400, NULL, NULL,\
hInstance, NULL
Zwróć uwagę, iż styl okna to tylko WS_POPUP, co utworzy okno bez brzegów i paska tytułowego. Rozmiary okna ustalamy na 320x400, aby obszar roboczy obejmował w całości nasz obrazek. Teraz gdy okno zostało utworzone, w sekcji obsługi wiadomości WM_CREATE przenosimy okno na środek ekranu za pomocą poniższego kodu:
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
Pobiera on wymiary pulpitu i okna, a następnie oblicza odpowiednie współrzędne lewego górnego narożnika okna, aby je umieścić na środku. Stosujemy następujące wzory:
x = (szerokość pulpitu - szerokość okna) / 2y = (wysokość pulpitu - wysokość okna) / 2
INVOKE LoadBitmap, hInstance, ADDR BitmapName
mov hBitMap, eax
INVOKE SetTimer, hWnd, 1, 2000, NULL
mov TimerID, eax
Następnie ładuje grafikę rastrową z zasobów za pomocą funkcji LoadBitmap i tworzy licznik czasu z numerem identyfikacyjnym ID równym 1 o okresie czasu powiadamiania 2 sekundy. Licznik czasu będzie co 2 sekundy wysyłał do tego okna wiadomości WM_TIMER.
.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 BitBlt, hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hMemoryDC, 0, 0, SRCCOPY
INVOKE SelectObject, hMemoryDC, hOldBmp
INVOKE DeleteDC, hMemoryDC
INVOKE EndPaint, hWnd, ADDR ps
Gdy okno otrzyma wiadomość WM_PAINT, tworzy ono kontekst urządzenia w pamięci, wprowadza grafikę do tego kontekstu, odczytuje jej wymiary przy pomocy funkcji GetObject, a następnie umieszcza grafikę w obszarze roboczym okna wywołując funkcje BitBlt. Po wykonaniu tych operacji usuwamy kontekst urządzenia w pamięci.
.ELSEIF uMsg==WM_LBUTTONDOWN
INVOKE DestroyWindow, hWnd
Użytkownik może się zdenerwować, jeśli będzie musiał czekać bezczynnie, aż ekran startowy zniknie z pulpitu. Dlatego dajemy mu wybór. Gdy kliknie lewym przyciskiem myszki na ekranie startowym, zniknie on. Dlatego właśnie obsługujemy wiadomość WM_LBUTTONDOWN w bibliotece DLL. Po odebraniu tej wiadomości okno jest usuwane za pomocą wywołania DestroyWindow.
.ELSEIF uMsg==WM_TIMER
INVOKE SendMessage, hWnd, WM_LBUTTONDOWN, NULL, NULL
INVOKE KillTimer, hWnd, TimerID
Jeśli użytkownik zdecyduje się poczekać, ekran startowy zniknie po upływie zadanego okresu czasu (w naszym przykładzie jest to 2 sekundy). Osiągamy ten efekt obsługując wiadomość WM_TIMER. Po jej odebraniu zamykamy okno wysyłając do niego wiadomość WM_LBUTTONDOWN. To w celu uniknięcia dublowania kodu. Nie korzystamy już dalej z licznika czasu, zatem usuwamy go za pomocą KillTimer.
Gdy okno zamknie się, biblioteka zwróci sterowanie do głównego programu.
|
Autorem
kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną
zgodą autora. |
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe