На этом шаге мы рассмотрим пример использования таймера.
Первый пример представляет простейший пример таймера. Таймер отсчитывает десять тиков и закрывает диалоговое окно, выдавая MessageBox с сообщением об окончании работы программы. Данная программа являет собой пример организации таймера на базе самой функции окна.
Заметим, что начиная с этого шага большинство приводимых программ могут транслироваться и в пакете MASM32, и в пакете TASM32 без всяких изменений.
Файл pr60_1.rc:
//Определение констант. #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L //Стиль - кнопка. #define BS_PUSHBUTTON 0x00000000L //Кнопка в окне должна быть видимой. #define WS_VISIBLE 0x10000000L //Центрировать текст на кнопке. #define BS_CENTER 0x00000300L //Стиль кнопки. #define WS_CHILD 0x40000000L //Возможность фокусировать элемент //при помощи клавиши TAB. #define WS_TABSTOP 0x00010000L #define DS_3DLOOK 0x0004L //Определение диалогового окна. DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK CAPTION "Пример диалогового окна с таймером" FONT 8, "Arial" { //Кнопка, идентификатор 5. CONTROL "Выход", 5, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 180, 76, 50, 14 }
Файл pr60_1.inc:
;Константы. ;Сообщение приходит при закрытии окна. WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_COMMAND equ 111h WM_TIMER equ 113h ;Прототипы внешних процедур. IFDEF MASM EXTERN ReleaseDC@8:NEAR EXTERN GetDC@4:NEAR EXTERN TextOutA@20:NEAR EXTERN MessageBoxA@16:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SendMessageA@16:NEAR EXTERN SetTimer@16:NEAR EXTERN KillTimer@8:NEAR ELSE EXTERN ReleaseDC:NEAR EXTERN GetDC:NEAR EXTERN TextOutA:NEAR EXTERN MessageBoxA:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SendMessageA:NEAR EXTERN SetTimer:NEAR EXTERN KillTimer:NEAR ReleaseDC@8 = ReleaseDC GetDC@4 = GetDC TextOutA@20 = TextOutA MessageBoxA@16 = MessageBoxA ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog SendMessageA@16 = SendMessageA SetTimer@16 = SetTimer KillTimer@8 = KillTimer ENDIF ;Структуры. ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS
Файл pr60_1.asm:
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr60_1.inc ;Директивы компоновщику для подключения библиотек. IFDEF MASM ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\gdi32.lib ELSE ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ;Дескриптор приложения. PA DB 'DIAL1',0 COUNT DB 0 TEXT DB 0 CAP DB 'Сообщение',0 MES DB 'Выход по таймеру',0 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL ;Здесь можно разместить сообщение об ошибке. KOL: ;------------------------------------- PUSH 0 CALL ExitProcess@4 ;------------------------------------- ;------------------------------------- ;Процедура окна. ;Расположение параметров в стеке: ;[ЕВР+014Н] LPARAM ;[ЕВР+10Н] WAPARAM ;[ЕВР+0СН] MES ;[ЕВР+8] HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI ;------------------------------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 ;Здесь реакция на закрытие окна. L3: ;Удалить таймер. PUSH 1 ;Идентификатор таймера. PUSH DWORD PTR [EBP+08H] CALL KillTimer@8 ;Закрыть диалог. PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L5 ;Здесь начальная инициализация. ;Установить таймер. PUSH 0 ;Параметр = NULL PUSH 1000 ;Интервал 1 с PUSH 1 ;Идентификатор таймера PUSH DWORD PTR [EBP+08H] CALL SetTimer@16 JMP FINISH L5: CMP DWORD PTR [EBP+0CH],WM_COMMAND JNE L2 ;Кнопка выхода? CMP WORD PTR [EBP+10H],5 JNE FINISH JMP L3 L2: CMP DWORD PTR [EBP+0CH],WM_TIMER JNE FINISH ;Не пора ли заканчивать? CMP COUNT,9 ;Выход без предупреждения. JA L3 ;Выход через сообщение. JE L4 ;Пришло сообщение таймера. ;Подготовить текст. MOV AL,COUNT ADD EAX,49 MOV TEXT,AL ;Получить контекст. PUSH DWORD PTR [EBP+08H] CALL GetDC@4 ;Запомнить контекст. PUSH EAX ;Вывести значение счетчика. PUSH 1 PUSH OFFSET TEXT PUSH 10 PUSH 10 PUSH EAX CALL TextOutA@20 ;Удалить контекст. POP EAX PUSH EAX PUSH DWORD PTR [EBP+08H] CALL ReleaseDC@8 ;Увеличить счетчик. INC COUNT JMP FINISH L4: INC COUNT ;Сообщение о выходе по таймеру. PUSH 0 PUSH OFFSET CAP PUSH OFFSET MES PUSH DWORD PTR [EBP+08H] ;Дескриптор окна. CALL MessageBoxA@16 JMP L3 FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX,0 RET 16 WNDPROC ENDP _TEXT ENDS END START
Результат работы приложения изображен на рисунке 1:
Рис.1. Результат работы приложения
Прокомментируем эту программу. Организация таймера здесь проста и очевидна. Единственное, что может вызвать трудность понимания, это то, как удается оставить MessageBox и одновременно закрыть диалоговое окно. Но здесь тоже все достаточно просто: сообщение появляется при COUNT=9, а когда приходит следующее сообщение, то COUNT уже больше 9, и выполняется ветка закрытия диалогового окна.
Трансляция программы:
ML /с /coff /DMASM pr60_1.asm RC pr60_1.rc LINK /SUBSYSTEM:WINDOWS pr60_1.obj pr60_1.res
TASM32 /ml pr60_1.asm BRCC32 pr60_1.rc TLINK32 -aa pr60_1.obj ,,,,, pr60_1.res
На следующем шаге мы приведем еще один пример использования таймера.