Шаг 60.
Простейший пример использования таймера

    На этом шаге мы рассмотрим пример использования таймера.

    Первый пример представляет простейший пример таймера. Таймер отсчитывает десять тиков и закрывает диалоговое окно, выдавая 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, и выполняется ветка закрытия диалогового окна.

    Трансляция программы:

    На следующем шаге мы приведем еще один пример использования таймера.




Предыдущий шаг Содержание Следующий шаг