Шаг 61.
Взаимодействие таймеров

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

    Следующая программа реализует два таймера. Можно считать, что запускаются одновременно две задачи (вообще говоря, три, так как само диалоговое окно также работает независимо). Одна задача с периодичностью 0.5 с получает системное время и формирует строку для вывода (STRCOPY). Эта задача имеет свою собственную функцию, на которую приходит сообщение WM_TIMER. Вторая задача работает в рамках функции окна. Эта задача с периодичностью 1 с выводит время и дату в окно редактирования, расположенное на диалоговом окне. Таким образом, две задачи взаимодействуют друг с другом посредством глобальной переменной STRCOPY.

    Еще один важный момент хотелось бы отметить в связи с данной программой. Поскольку на функцию таймера приходит сообщение, в котором указан идентификатор таймера, мы можем на базе одной функции реализовать любое количество таймеров.

    Файл pr61_1.rc:

//Определение констант.
#define WS_SYSMENU         0x00080000L
//Элементы на окне должны быть изначально видимы.
#define WS_VISIBLE         0x10000000L
//Бордюр вокруг элемента.
#define WS_BORDER          0x00800000L
//Возможность фокусировать  элемент
//при помощи клавиши TAB.
#define WS_TABSTOP         0x00010000L
//Текст в окне редактирования прижат к левому краю.
#define ES_LEFT            0x0000L
//Стиль всех элементов на окне.
#define WS_CHILD           0x40000000L
//Запрещается ввод с клавиатуры.
#define ES_READONLY        0x0800L
#define DS_3DLOOK          0x0004L
//Определение диалогового окна. 
DIAL1  DIALOG  0,  0,  240, 100
STYLE WS_SYSMENU | DS_3DLOOK 
CAPTION  "Диалоговое окно с часами и датой" 
FONT 8,   "Arial" 
{
  CONTROL  "",   1,    "edit",   ES_LEFT | WS_CHILD 
     | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_READONLY, 
     100, 5, 130, 12
}

    Файл pr61_1.inc:

;Константы.
;Сообщение приходит при закрытии окна.
WM_CLOSE      equ 10h
WM_INITDIALOG equ 110h
;Сообщение приходит при событии с элементом на окне.
WM_COMMAND    equ 111h
;Сообщение от таймера.
WM_TIMER      equ 113h
;Сообщение посылки текста элементу.
WM_SETTEXT    equ 0Ch
;Прототипы внешних процедур.
IFDEF MASM
    EXTERN     SendDlgItemMessageA@20:NEAR 
    EXTERN     wsprintfA:NEAR 
    EXTERN     GetLocalTime@4:NEAR 
    EXTERN     ExitProcess@4:NEAR 
    EXTERN     GetModuleHandleA@4:NEAR 
    EXTERN     DialogBoxParamA@20:NEAR
    EXTERN     EndDialog@8:NEAR 
    EXTERN     SetTimer@16:NEAR 
    EXTERN     KillTimer@8:NEAR
ELSE
    EXTERN     SendDlgItemMessageA:NEAR 
    EXTERN     _wsprintfA:NEAR 
    EXTERN     GetLocalTime:NEAR 
    EXTERN     ExitProcess:NEAR 
    EXTERN     GetModuleHandleA:NEAR 
    EXTERN     DialogBoxParamA:NEAR

    EXTERN     EndDialog:NEAR
    EXTERN     SetTimer:NEAR
    EXTERN     KillTimer:NEAR

    SendDlgItemMessageA@20 = SendDlgltemMessageA 
    wsprintfA              = _wsprintfA 
    GetLocalTime@4         = GetLocalTime 
    ExitProcess@4          = ExitProcess 
    GetModuleHandleA@4     = GetModuleHandleA 
    DialogBoxParamA@20     = DialogBoxParamA 
    EndDialog@8            = EndDialog 
    SetTimer@16            = SetTimer 
    KillTimer@8            = KillTimer 
ENDIF
;Структуры.
;Структура сообщения.
MSGSTRUCT  STRUC	
           MSHWND     DD ? ;Идентификатор окна, получающего сообщение.
           MSMESSAGE  DD ? ;Идентификатор сообщения.
           MSWPARAM   DD ? ;Доп. информация о сообщении.
           MSLPARAM   DD ? ;Доп. информация о сообщении.
           MSTIME     DD ? ;Время посылки сообщения.
           MSPT       DD ? ;Положение курсора во время посылки сообщения.
MSGSTRUCT ENDS	
;Cтруктура данных дата-время.
DAT        STRUC		
           year    DW   ?	
           month   DW   ?	
           dayweek DW   ?	
           day     DW   ?	
           hour    DW   ?	
           min     DW   ?	
           sec     DW   ?	
           msec    DW   ?	
DAT ENDS		

    Файл pr61_1.asm:

.386P
;Плоская модель.
.MODEL FLAT, STDCALL
include pr61_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
     TIM        DB "Дата  %u/%u/%u Время  %u:%u:%u",0
     STRCOPY    DB  50   DUP(?) 
     DATA       DAT  <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
;Здесь реакция на закрытие окна. 
;Удалить таймер 1.
        PUSH 1   ;Идентификатор таймера. 
        PUSH DWORD  PTR  [EBP+08H] 
        CALL KillTimer@8 
;Удалить таймер 2.
        PUSH 2   ;Идентификатор таймера. 
        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  L2
;Здесь начальная инициализация. 
;Установить таймер 1.
        PUSH 0      ;Параметр = NULL 
        PUSH 1000   ;Интервал  1 с 
        PUSH 1      ;Идентификатор таймера 
        PUSH DWORD  PTR  [EBP+08H] 
        CALL SetTimer@16 
;Установить таймер 2.
        PUSH OFFSET TIMPROC  ;Параметр = NULL 
        PUSH 500             ;Интервал 0.5 с 
        PUSH 2               ;Идентификатор таймера 
        PUSH DWORD  PTR  [EBP+08H] 
        CALL SetTimer@16 
        JMP  FINISH 
L2:
        CMP  DWORD PTR [EBP+0CH],WM_TIMER 
        JNE  FINISH 
;Отправить строку в окно. 
        PUSH OFFSET  STRCOPY 
        PUSH 0
        PUSH WM_SETTEXT
        PUSH 1  ;Идентификатор элемента. 
        PUSH DWORD PTR [EBP+08H] 
        CALL SendDlgItemMessageA@20 
FINISH:
        POP  EDI
        POP  ESI
        POP  EBX
        POP  EBP
        MOV  EAX,0
        RET  16
WNDPROC ENDP
;-------------------------------------
;Процедура таймера. 
;Расположение параметров в стеке.
;[ЕВР+014Н]   LPARAM - промежуток запуска Windows.
;[ЕВР+10Н]    WAPARAM - идентификатор таймера.
;[EBP+0CH]    WM_TIMER
;[ЕВР+8]      HWND
TIMPROC PROC 
        PUSH EBP 
        MOV  EBP,ESP
;Получить локальное время. 
        PUSH OFFSET DATA 
        CALL GetLocalTime@4
;Получить  строку для вывода даты и времени. 
        MOVZX EAX, DATA.sec 
        PUSH EAX
        MOVZX EAX, DATA.min 
        PUSH EAX
        MOVZX EAX,DATA.hour 
        PUSH EAX 
        MOVZX EAX,DATA.year
        PUSH EAX
        MOVZX EAX,DATA.month
        PUSH EAX
        MOVZX EAX,DATA.day
        PUSH EAX
        PUSH OFFSET TIM
        PUSH OFFSET STRCOPY
        CALL  wsprintfA 
;Восстановить стек.
        ADD  ESP,32
        POP  EBP
        RET  16
TIMPROC ENDP 
_TEXT   ENDS	
        END START
Текст этой программы можно взять здесь.

    Результат работы приложения изображен на рисунке 1:


Рис.1. Результат работы приложения

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

    Обращаем ваше внимание на весьма полезную функцию GetLocalTime. Информация, полученная с помощью этой функции (см. структуру DAT), легко может быть использована для самых разных целей, в том числе и для вывода на экран. Аналогично, с помощью функции SetLocalTime, вы сможете установить текущее время. Для получения времени по Гринвичу используется функция GetSystemTime, которая, соответственно, С помощью SetSystemTime используется для установки времени в Гринвичском выражении. Аргументом во всех этих функциях является уже упомянутая выше структура (точнее указатель на нее).

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




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