API и FreeBasic. (Сплэш-экран)
Сплэш-экран - это окно, у которого нет заголовка, нет системных кнопок, нет border'а, которое отображает битмап на некоторое время и затем исчезает. Обычно оно используется во время загрузки программы, чтобы отображать лого программы или отвлечь внимание пользователя, пока программа делает объемную инициализацию. В этом туториале мы создадим сплэш-экран.
Первый шаг - это прописать битмап в файле ресурсов. Тем не менее, если это важно для вас, то загружать битмап, который будет использоваться только один раз, и держать его в памяти, пока программа не будет закрыта, пустая трата ресурсов. Лучшим решением является ресурсовую DLL, которая будет содержать битмап, и чьей целью является отображение сплэш-экрана. В этом случае вы сможете загрузить DLL, когда вам нужно отобразить сплэш-экран, и выгрузить ее, как только нужда в ней отпадает. Поэтому у нас будет два модуля: основная программа и сплэш-экран. Мы поместим битмап в файл ресурсов DLL.
Общая схема такова:
- Поместить битмап в DLL как ресурс.
- Основная программа вызывает LoadLibrary, чтобы загрузить dll в память.
- Запускается входная функция DLL. Она создаст таймеp и установит время, в течении которого будет отображаться сплэш-экран. Затем она зарегистрирует и создаст окно без заголовка и бордера, после чего отобразит битмап в клиенсткой области.
- Когда закончится указанный период времени, сплэш-экран будет убран с экрана и контроль будет передан главной программе.
- Основная программа вызовет FreeLibrary, чтобы выгрузить DLL из памяти, а затем перейдет к выполнению того, к чему она предназначена.
Мы детально проанализируем описанную последовательность действий.
Загрузка/выгрузка DLL
Вы можете динамически загрузить DLL с помощью функции LoadLibrary, которая имеет следующий синтаксис:
LoadLibrary( lpLibFileName As LPCTSTR) As HINSTANCE
Она принимает только один параметр: адрес имени DLL, который вы хотите загрузить в память. Если вызов пройдет успешно, он возвратит хэндл модуля DLL, в противном случае NULL.
Чтобы выгрузить DLL, вызовите FreeLibrary:
FreeLibrary( lpLibFileName As HMODULE) As BOOL
Она получает один параметр: хэндл модуля DLL, которую вы хотите выгрузить.
Как использовать таймеp
Во-первых, мы должны создать таймер с помощью функции SetTimer:
SetTimer( _ hWnd As HWND, _ nIDEvent As Uinteger, _ uElapse As Uinteger, _ TIMERPROC lpTimerFunc) As Uinteger
- hWnd - хэндл окна, которое будет получать уведомительные сообщения от таймера. Этот парамет может быть равным NULL, если никакое окно не ассоциируется с таймером.
- TimerID - заданное пользователем значение, которое будет использоваться в качестве ID таймера.
- uElaрse - временной интервал в миллисекундах.
- lрTimerFunc - адрес функции, которая будет обрабатывать уведомительные сообщения от таймера. Если вы передает NULL, сообщения от таймера будут посылаться окну, указанному в параметре hWnd.
- SetTimer возвращает ID таймера, если вызов прошел успешно, иначе она возвратит NULL. Поэтому лучше не использовать ноль в качестве ID таймера.
Вы можете создать таймеp двумя путями:
- Если у вас есть окно и вы хотите, чтобы сообщения от таймера посылались окну, вы должны передать все четыре параметра SetTimer (lpTimerFunc должен быть pавен NULL).
- Если у вас нет окна или вы не хотите обрабатывать сообщения таймера в процедуре окна, вы должны передать NULL функции вместо хэндла окна. Вы также должны указать адрес функции таймера, которая будет обрабатывать его сообщения.
В этом туториале мы используем первый подход.
Каждый раз за указанный вами временной интервал окну, ассоциированному с таймером, будет посылаться сообщение WM_TIMER. Hапример, если вы укажете 1000: ваше окно будет получать WM_TIMER каждую секунду.
Когда вам больше не нужен таймеp, уничтожьте его с помощью KillTimer:
KillTimer( _ hWnd As HWND, _ uIDEvent As Uinteger _ )As BOOL
Пpимеp:
''''''''''''''''''''''''''''''' 'Основная программа ''''''''''''''''''''''''''''''' #INCLUDE "windows.bi" Declare Function WinMain(hInst As HINSTANCE , hPrevInst As HINSTANCE,CmdLine As LPSTR,CmdShow As DWORD) As Integer Declare Function WndProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer #DEFINE IDB_MAIN 1 Dim Shared As String ClassName,AppName,Libname Dim As HINSTANCE hinst Dim ShowBitMap As FARPROC ClassName = "SplashDemoWinClass" AppName = "Splash Screen Example" Libname = "dll/Splash.dll" Dim Shared hInstance As HINSTANCE Dim Shared CommandLine As LPSTR hinst = LoadLibrary(Strptr(Libname)) If hinst <>0 Then ShowBitMap = GetProcAddress(hinst, "SHOWBITMAP@0") If ShowBitMap<>0 Then ShowBitMap() FreeLibrary(hinst) Endif hInstance = GetModuleHandle(NULL) CommandLine = GetCommandLine() ExitProcess(WinMain(hInstance,NULL,CommandLine, SW_SHOWDEFAULT)) Function WinMain(hInst As HINSTANCE , hPrevInst As HINSTANCE,CmdLine As LPSTR,CmdShow As DWORD) As Integer Dim wc As WNDCLASSEX Dim msg As MSG Dim hwnd As HWND wc.cbSize = Sizeof(WNDCLASSEX) wc.style = CS_HREDRAW Or CS_VREDRAW wc.lpfnWndProc = @WndProc wc.hInstance = hInstance wc.hbrBackground = Cast(HBRUSH, COLOR_WINDOW+1) wc.lpszClassName = Strptr(ClassName) wc.hIcon = LoadIcon(NULL,IDI_APPLICATION) wc.hIconSm = wc.hIcon wc.hCursor = LoadCursor(NULL,IDC_ARROW) RegisterClassEx(@wc) hwnd = CreateWindowEx(NULL,ClassName,AppName, _ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, _ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL, _ hInst,NULL) ShowWindow(hwnd,SW_SHOWNORMAL) UpdateWindow(hwnd) While GetMessage(@msg,NULL,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend Return msg.wParam End Function Function WndProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer If uMsg=WM_DESTROY Then PostQuitMessage(NULL) Else Return DefWindowProc(hWnd,uMsg,wParam,lParam) Endif Return 0 End Function
''''''''''''''''''''''''''' 'Splash.dll ''''''''''''''''''''''''''' #INCLUDE "windows.bi" Declare Function WndProc(hWnd As HWND,uMsg As UINT,wParam As WPARAM,lParam As LPARAM) As Integer Dim Shared As String BitmapName,ClassName Dim Shared As Uinteger TimerID Dim Shared As HBITMAP hBitMap Dim Shared As HINSTANCE hInstance BitmapName = "MySplashBMP" ClassName = "SplashWndClass" Sub ShowBitMap() Export hInstance = GetModuleHandle("Splash.dll") Dim wc As WNDCLASSEX Dim msg As MSG Dim hwnd As HWND wc.cbSize = Sizeof(WNDCLASSEX) wc.style = CS_HREDRAW Or CS_VREDRAW wc.lpfnWndProc = @WndProc wc.hInstance = hInstance wc.hbrBackground = Cast(HBRUSH, COLOR_WINDOW+1) wc.lpszClassName = Strptr(ClassName) wc.hIcon = LoadIcon(NULL,IDI_APPLICATION) wc.hCursor = LoadCursor(NULL,IDC_ARROW) RegisterClassEx(@wc) hwnd = CreateWindowEx(NULL,Strptr(ClassName),NULL, _ WS_POPUP,CW_USEDEFAULT, _ CW_USEDEFAULT,250,250,NULL,NULL, _ hInstance,NULL) ShowWindow(hwnd,SW_SHOWNORMAL) While GetMessage(@msg,NULL,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend End Sub Function WndProc(hWnd As HWND,uMsg As UINT,wParam As WPARAM,lParam As LPARAM) As Integer Dim ps As PAINTSTRUCT Dim hdc As HDC Dim hMemoryDC As HDC Dim hOldBmp As HBITMAP Dim bitmap As BITMAP Dim DlgHeight As DWORD Dim DlgWidth As DWORD Dim DlgRect As RECT Dim DesktopRect As RECT If uMsg=WM_DESTROY Then If hBitMap<>0 Then DeleteObject(hBitMap) PostQuitMessage(NULL) Elseif uMsg=WM_CREATE Then GetWindowRect(hWnd,@DlgRect) GetWindowRect(GetDesktopWindow,@DesktopRect) DlgHeight = DlgRect.bottom - DlgRect.top DlgWidth = DlgRect.right - DlgRect.left MoveWindow(hWnd ,(DesktopRect.right - DlgWidth)\2,(DesktopRect.bottom - DlgHeight)\2 ,DlgWidth,DlgHeight,0) hBitMap = LoadBitmap(hInstance,BitmapName) TimerID = SetTimer(hWnd,1,2000,NULL) Elseif uMsg=WM_TIMER Then SendMessage(hWnd,WM_LBUTTONDOWN,NULL,NULL) KillTimer(hWnd,TimerID) Elseif uMsg=WM_PAINT Then hdc = BeginPaint(hWnd,@ps) hMemoryDC = CreateCompatibleDC(hdc) hOldBmp = SelectObject(hMemoryDC,hBitMap) GetObject(hBitMap,Sizeof(BITMAP),@bitmap) StretchBlt(hdc,0,0,250,250, _ hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY) SelectObject(hMemoryDC,hOldBmp) DeleteDC(hMemoryDC) EndPaint(hWnd,@ps) Elseif uMsg=WM_LBUTTONDOWN Then DestroyWindow(hWnd) Else Return DefWindowProc(hWnd,uMsg,wParam,lParam) Endif End Function
Файл ресурсов Splash.rc
MySplashBMP BITMAP "JourneyStart.bmp"
Рисунок для проекта:
Анализ:
Примечание: В связи с особенностью языка, техника вызова DLL немного изменена.
Сначала мы проанализируем код основной программы.
hinst = LoadLibrary(Strptr(Libname)) If hinst <>0 Then ShowBitMap = GetProcAddress(hinst, "SHOWBITMAP@0") If ShowBitMap<>0 Then ShowBitMap() FreeLibrary(hinst) Endif
Мы вызовем LoadLibrary, чтобы загрузить DLL "Sрlash.dll". Далее получаем адрес на процедуру ShowBitMap и если вызов не закончился неудачей, то вызываем эту функцию. После этого выгружаем ее из памяти функцией FreeLibrary. LoadLibrary не возвратится, пока DLL не закончит свою инициализацию.
Это все, что делает основная программа. Интересующая нас часть находится в DLL.
При вызове ShowBitmaр, мы получаем хендл (своего )модуля DLL. Далее регистрируется класс окна, создает окно и входит в цикл обработки сообщений. Следует обратить внимание на вызов CreateWindowEx:
hwnd = CreateWindowEx(NULL,Strptr(ClassName),NULL, _
WS_POPUP,CW_USEDEFAULT, _
CW_USEDEFAULT,250,250,NULL,NULL, _
hInstance,NULL)
Обратите внимание, что стиль окна WS_РOРUР, что делает окно без бордюра и без заголовка. Мы также ограничиваем размер окна - 250x250.
Теперь, когда окно создано, в обработчике WM_CREATE мы передвигаем окно в центр экрана следующим кодом:
GetWindowRect(hWnd,@DlgRect) GetWindowRect(GetDesktopWindow,@DesktopRect) DlgHeight = DlgRect.bottom - DlgRect.top DlgWidth = DlgRect.right - DlgRect.left MoveWindow(hWnd , _ (DesktopRect.right - DlgWidth)\2, _ (DesktopRect.bottom - DlgHeight)\2 , _ DlgWidth,DlgHeight,0)
Мы получаем размеры десктопа и окан, а затем вычисляем координаты левого
верхнего угла окна, чтобы оно было в центре.
hBitMap = LoadBitmap(hInstance,BitmapName) TimerID = SetTimer(hWnd,1,2000,NULL)
Затем мы загружаем битмап из ресурса функцией LoadBitmap и создаем
таймеp, указывая в качестве его ID 1, а в качестве временного интервала 2
секунды. Таймеp будет посылать сообщения WM_TIMER окну каждый две секунды.
Elseif uMsg=WM_PAINT Then hdc = BeginPaint(hWnd,@ps) hMemoryDC = CreateCompatibleDC(hdc) hOldBmp = SelectObject(hMemoryDC,hBitMap) GetObject(hBitMap,Sizeof(BITMAP),@bitmap) StretchBlt(hdc,0,0,250,250, _ hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY) SelectObject(hMemoryDC,hOldBmp) DeleteDC(hMemoryDC) EndPaint(hWnd,@ps)
Когда окно получит сообщение WM_РAINT, она создаст DC в памяти, выберет в него битмап, получит pазмеp битмапа функцией GetObject, а затем поместит битмап на окно, вызвав StretchBlt, которая действует как BitBlt, но адаптирует битмап к желаемым размерам. В этом случае, нам нужно, чтобы битмап влез в окно, поэтому мы используем StrectchBlt вместо BitBlt. Мы удаляем созданный в памяти DC.
Elseif uMsg=WM_LBUTTONDOWN Then DestroyWindow(hWnd)
Пользователя бы раздражало, если бы ему пришлось бы ждать, пока сплэш-экран не исчезнет. Мы можем предоставить пользователю выбор. Когда он кликнет на сплэш-экране, тот исчезнет. Вот почему нам нужно обрабатывать сообщение WM_LBUTTONDOWN. Когда мы получим это сообщение, окно будет уничтожено вызовом DestroyWindow.
Elseif uMsg=WM_TIMER Then SendMessage(hWnd,WM_LBUTTONDOWN,NULL,NULL) KillTimer(hWnd,TimerID)
Если пользователь решит подождать, сплэш-экран исчезнет, когда пройдет заданный период времени (в нашем примере, это две секунды). Мы можем сделать это обработкой сообщения WM_TIMER. После получения этого сообщения, мы закрываем окно, послав ему сообщение WM_LBUTTONDOWN, чтобы избежать повторения кода. Таймер нам больше не нужен, поэтому мы уничтожаем его KillTimer.
Когда окно будет закрыто, DLL возвратит контроль основной программе.
По окончанию работы удаляем битмэп посредством DeleteObject.
Перевод на русский с оригинала: Aquila, адаптация материалов под FreeBasic: Станислав Будинов
содержание | назад | вперед