На этом шаге мы рассмотрим еще одну программу с использованием таймера.
Следующая программа реализует два таймера. Можно считать, что запускаются одновременно две задачи (вообще говоря, три, так как само диалоговое окно также работает независимо). Одна задача с периодичностью 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. Результат работы приложения
Трансляция программы:
ML /с /coff /DMASM pr61_1.asm RC pr61_1.rc LINK /SUBSYSTEM:WINDOWS pr61_1.obj pr61_1.res
TASM32 /ml pr61_1.asm BRCC32 pr61_1.rc TLINK32 -aa pr61_1.obj ,,,,, pr61_1.res
Обращаем ваше внимание на весьма полезную функцию GetLocalTime. Информация, полученная с помощью этой функции (см. структуру DAT), легко может быть использована для самых разных целей, в том числе и для вывода на экран. Аналогично, с помощью функции SetLocalTime, вы сможете установить текущее время. Для получения времени по Гринвичу используется функция GetSystemTime, которая, соответственно, С помощью SetSystemTime используется для установки времени в Гринвичском выражении. Аргументом во всех этих функциях является уже упомянутая выше структура (точнее указатель на нее).
На следующем шаге мы рассмотрим создание всплывающих подсказок.