Tutorial 26: Splash Screen
이전 장을 읽었다면 비트맵에 대한 지식이 있을 것이므로, 이 번장에서는 비트맵을 응용하여 「스플래쉬 스크린」에 대해서 설명한다.
메인 소스 | 비트 맵 | 실행 결과 |
DLL 소스 | DEF 파일 | 리소스 |
|
스플래쉬 스크린이라는 것은 타이틀바와 시스템메뉴를 가지지 않는 윈도우로서 비트맵을 일정시간 표시한 후, 자동으로 소멸되는 윈도우이다. 프로그램의 시작 시에 자주 사용되고 로고를 표시하거나 실행시간이 길어질 경우 유저로 하여금 동작중임을 알리는 역활도 하게된다. 이 번장에서는 스플래쉬 스크린을 실제로 작성해 본다.
먼저, resource file에 비트맵을 추가한다. 그러나 이런 경우, 프로그램이 종료할 때까지 비트맵을 로드하고 있기 때문에 메모리를 낭비하게 된다. 해결방법은, 비트맵을 포함하는 리소스 DLL을 작성하는 것이다.리소스 DLL은, 단지 스플래쉬 스크린을 저장하는 역활만 하게 된다. 이런 방법을 이용하면, 화면에 표시하고 싶은때에만 DLL을 로드하고, 필요없어 지면 언로드를 수행해서 메모리에서 해제하게 되서 메모리 효율성이 높아진다. 당연히, 2개의 모듈이 필요하게 된다. 한개는 메인 프로그램이고, 다른 하나는 스플래쉬 DLL로서, DLL에 비트맵을 저장한다.
사용하는 방법은 다음과 같다.
- 비트맵 리소스로서 DLL에 비트맵을 저장한다
- 메인 프로그램에서, LoadLibrary 함수를 호출해 DLL을 로드한다
- DLL의 엔트리 포인트 함수가 실행된다. 타이머를 생성하고, 스플래쉬 스크린이 일정시간 동안 표시되는지를 지정한다. 다음으로, 타이틀바와 경계선이 없는 윈도우를 등록한 후, 생성해서, 작업영역에 비트맵을 표시한다.
- 일정한 시간이 지나면, 스플래쉬 스크린은 화면에서 사라지고, 제어가 메인 프로그램에 돌아온다.
- 메인 프로그램에서, FreeLibrary 함수를 호출해서, 메모리로부터 DLL를 해제한다. 그후, 자신의 처리를 계속한다.
그럼, 단계별로 설명한다.
●DLL의 로드/언로드
LoadLibrary 함수를 호출함으로써 동적으로 DLL을 로드할 수가 있다.
LoadLibrary proto lpDLLName:DWORD이 함수는 단지 하나의 인수만 가지며,로드하고 싶은 DLL의 문자열을 설정한다. 호출이 성공적으로 수행되면 DLL의 모듈핸들이 반환되고,실패하게되면 NULL이 반환된다.
DLL를 언로드하려면 , FreeLibrary 함수를 호출한다.
FreeLibrary proto hLib:DWORD이 함수역시 한개의 인수를 가지며, 언로드하고 싶은 DLL의 모듈핸들을 설정한다. 보통, LoadLibrary 함수로부터 얻은 핸들이다.
●타이머의 사용법
타이머를 사용하려면 먼저, SetTimer 함수를 호출한다.
SetTimer proto hWnd:DWORD, TimerID:DWORD, uElapse:DWORD, lpTimerFunc:DWORD
- hWnd
타이머로부터 통지메세지를 받는 윈도우핸들. 타이머로부터 통지를 받는 윈도우가 없을 경우는 NULL을 설정.- TimerID
타이머ID로서, 유저가 자유롭게 정의할 수 있다.- uElapse
타이머 메세지의 발생주기를 밀리 세컨드로 지정한다.(1000 == 1 초)- lpTimerFunc
타이머메세지가 발생할 경우 실행되는 함수의 포인터(타이머전용 처리함수). NULL을 지정하면, 타이머 메세지는 hWnd에 지정된 윈도우로 전송된다.함수가 성공적으로 수행되면, SetTimer 함수는 타이머ID를 반환하고, 실패한다면 NULL을 반환한다.
타이머를 생성하는 방법은 다음의 2가지가 있다.
- 타이머 메세지를 전송하고 싶은 윈도우가 있는 경우, SetTimer 함수에 모든 인수를 설정해야한다. (lpTimerFunc은 NULL로 설정)
- 윈도우가 없는 경우와 타이머 메세지를 윈도우 프로시저에서 처리하고 싶지 않은 경우에는, 윈도우 핸들 hWnd 에 NULL을 설정하고, 타이머 메세지를 처리하는 타이머함수의 포인터를 지정해야 한다.
예제에서는 처음의 방법을 사용한다.
일정한 시간이 경과했을때, WM_TIMER 메세지가 윈도우에 전송된다. 예를 들면, uElapse를 1000으로 하면, WM_TIMER 메세지가 지정한 윈도우로 1초 마다 전송된다.
타이머가 필요없다면, KillTimer 함수를 호출해서 타이머를 해제한다.(타이머도 시스템 자원이다)
KillTimer proto hWnd:DWORD, TimerID:DWORD
|
|
|
|
메인 프로그램은 다음 코드로 시작하고 있다.
invoke LoadLibrary, addr Libname .if eax != NULL invoke FreeLibrary, eax .endif이것은,"splash.dll"이라는 리소스DLL을 로드하는 코드이다. 그리고, FreeLibrary 함수로 언로드하고 있다. LoadLibrary 함수는 DLL의 초기화가 완료될 때까지 제어를 반환하지 않게 되어있다.
메인 프로그램에서의 처리는 이것이 전부이다. 실제적인 처리는 DLL에서 하고 있다.
.if reason==DLL_PROCESS_ATTACH ; When the dll is loaded push hInst pop hInstance call ShowBitMapDLL이 로드 되면, Windows는 DLL_PROCESS_ATTACH 플래그를 추가하고 엔트리 포인트 함수를 호출하도록 되어 있다. 이 때, 스플래쉬 스크린이 화면에 표시된다.다음으로 DLL의 인스턴스 핸들을 저장해 둔다. 그리고나서, ShowBitMap 함수를 호출해서 주된 처리를 한다. ShowBitMap 함수는 윈도우 클래스를 등록하고, 윈도우를 생성 하고 메시지 루프에 들어간다. CreateWindowEx 함수를 잠시 살펴보자.
INVOKE CreateWindowEx, NULL, ADDR ClassName, NULL,\ WS_POPUP, CW_USEDEFAULT,\ CW_USEDEFAULT, 250,250, NULL, NULL,\ hInstance, NULL여기서 주의할 것은 윈도우 스타일로,WS_POPUP 을 지정하고 있다. 이로인해, 윈도우의 경계선과 타이틀바가 없는 윈도우가 된다. 동시에 윈도우크기를 250×250으로 설정한다.
윈도우가 생성되면, 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다음으로, LoadBitmap 함수를 사용해서 리소스로부터 비트맵을 로드하고, 타이머ID가 1 이며, 2초마다(2000) 메세지를 발생하는 타이머를 생성한다. 이 타이머는 2초 마다 지정된 윈도우에 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 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윈도우가 WM_PAINT 메세지를 받게되면, 메모리DC를 생성하고, 메모리DC에 로드한 비트맵을 선택한다. 그런 후에, GetObject 함수로 비트맵의 크기를 얻은후, StretchBlt 함수를 사용해서 비트맵을 윈도우에 표시한다. StretchBlt 함수는 BitBlt 함수와 비슷하지만, 지정한 폭과 높이에 따라 크기를 확장하는 기능을 가지고 있다.예제에서는 생성한 윈도우에 딱맞도록 해야하기 때문에, BitBlt 함수가 아니고 StretchBlt 함수를 사용했다. 그 후, DC를 삭제한다.
.elseif uMsg==WM_LBUTTONDOWN invoke DestroyWindow, hWnd어떤 유저에게 있어서는 스플래쉬 스크린이 사라질 때까지 기다리는 것이 못 마땅할 수도있기 때문에, 유저가 스플래쉬 스크린을 클릭하게되면 바로 사라지도록했다. 이런 처리 때문에, DLL에서 WM_LBUTTONDOWN 메세지를 처리할 필요가 있다. 이 메세지를 받으면 DestroyWindow 함수로 윈도우를 파괴한다.
.elseif uMsg==WM_TIMER invoke SendMessage, hWnd, WM_LBUTTONDOWN, NULL, NULL invoke KillTimer, hWnd, TimerID유저가 아무런 일도 하지않는다면, 스플래쉬 스크린은 타이머가 지정한 시간이 되면 사라지게 되어 있다. 이는 WM_TIMER 메세지에서 처리하고 있지만, WM_LBUTTONDOWN 메세지에서 윈도우를 파괴하는 코드를 작성했으므로, 해당 메세지를 보내서 같은 코드를 사용하게 하였다.타이머는 이제 필요없기 때문에, KillTimer 함수를 사용해서 삭제한다.
윈도우가 화면에서 사라지면, DLL은 메인 프로그램에 제어를 반환하게 된다.