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: DWORD
Jeś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