2006 г.
Биллиг Владимир Арнольдович
Интернет-Университет Информационных Технологий, INTUIT.ru
Назад Оглавление Вперёд
Функции Win32 API для работы с таймером
Кроме функций перечисления, требующих в процессе своей работы вызов Callback
функций, другим известным примером является функция SetTimer
, создающая таймер. Во многих приложениях возникает необходимость синхронизировать его работу в соответствии с регулярно поступающими сообщениями от таймера. Общая схема такова: таймер посылает сообщения приложению с заданным интервалом, в ответ приложение выполняет определенную работу, вызывая ту или иную функцию (Callback
функцию). Класс таких диспетчерских приложений, регулярно обрабатывающих вновь поступившие заявки, весьма велик. При работе в приложении Access для этих целей введен специальный элемент управления - Timer
. В приложениях Word или Excel такого элемента нет, но всегда можно воспользоваться соответствующими функциями Win32 API, чтобы создать один или несколько собственных таймеров и организовать работу приложения, реагирующего на их сообщения. Заметьте, несмотря на то, что физический таймер один, логических таймеров, посылающих приложению свои сообщения, может быть несколько.
Функция SetTimer
Эта функция создает таймер, посылающий сообщения с заданным интервалом. Ее описание, которое можно найти на Platform SDK, имеет вид:
UINT SetTimer(
HWND hWnd, // handle to window for timer messages
UINT nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // pointer to timer procedure
);
Ее параметры:
hwnd
- Описатель окна, которому будут посылаться сообщения таймера. В VBA программах таймер не связывается с окном и значение этого параметр задается как NULL
. nIDEvent
- Задает идентификатор таймера. Его значение игнорируется, когда таймер не связан с окном, что имеет место в рассматриваемом нами случае.uElapse
- Задает интервал, с которым таймер будет посылать свои сообщения. Интервал задается в миллисекундах, так что значение 1000
соответствует одной секунде. lpTimerFunc
- Указатель на Callback
функцию, которая будет вызываться всякий раз, когда обрабатывается сообщение WM_Timer
, поступающее от таймера.
Если функция успешно завершает свою работу и создает таймер, то в качестве результата она возвращает уникальный идентификатор этого таймера, идентифицирующий его. Этот идентификатор запоминается и используется для уничтожения таймера при вызове Win32 API функции KillTimer
. В случае неуспеха возвращается значение 0
.
Заметьте, функция SetTimer
только создает таймер. В отличие от функций перечисления вызов Callback
функции не происходит в ее теле. Вызов осуществляется более сложным путем. Созданный таймер посылает сообщения с заданным интервалом, сообщения, как обычно, поступают в очередь сообщений и в обработчике сообщения WM_Timer
автоматически вызывается функция обратного вызова. Поскольку при обработке сообщений очереди могут происходить разные задержки, то вызываемая функция не всегда будет вызываться с заданным интервалом, - возможны задержки.
Оператор Declare
, задающий VBA описание этой функции имеет соответственно вид:
Public Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, _
ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Функция обратного вызова TimerProc
Функция TimerProc
является Callback
функцией, определенной приложением и вызываемой при обработке сообщений, поступающих от таймера. Ее определение имеет вид:
VOID CALLBACK TimerProc(
HWND hwnd, // handle of window for timer messages
UINT uMsg, // WM_TIMER message
UINT idEvent, // timer identifier
DWORD dwTime // current system time
);
Ее параметры:
hwnd
- Описатель окна.uMsg
- Указывает WM_Timer
сообщение.idEvent
- Идентификатор таймера. dwTime
- Задает текущее системное время, возвращаемое функцией GetTickCount
.
Заметьте, имя TimerProc
является лишь держателем места. В конкретной ситуации необходимо будет определить одну или несколько Callback
функций с подходящими именами. Поскольку вызов каждой из этих функций производится автоматически, то нет необходимости заботиться о корректной передаче аргументов в момент вызова. Необходимо лишь позаботиться о корректной трансляции приведенного определения, взятого из справочной системы Platform SDK, к виду, воспринимаемому в программах на VBA. Вот как выглядит возможное определение:
Public Sub TimerProc(ByVal HandleW As Long, ByVal msg As Long, _
ByVal idEvent As Long, ByVal TimeSys As Long)
Обратите внимание, мы транслировали функцию в процедуру, поскольку Callback
функция TimerProc
не возвращает значения. Все типы данных преобразованы в тип Long
, в том числе UINT
и DWORD
. В данной ситуации нет причин для беспокойства о возможной некорректности передаваемых значений, поскольку их передачу обеспечивает сама система.
Функция KillTimer
Поскольку одновременно могут существовать несколько таймеров, также как и при необходимости заменить один таймер другим, - по разным причинам возникает необходимость удаления уже не нужных таймеров. Для этого и используется функция Win32 API KillTimer
. Вот ее стандартное описание:
BOOL KillTimer(
HWND hWnd, // handle to window that installed timer
UINT nIDEvent, // timer identifier
);
Ее параметры:
hwnd
- Описатель окна, ассоциированного с таймером, совпадающий по значению с соответствующим параметром функции SetTimer
. Напомним, в VBA программах таймер не связывается с окном и значение этого параметр задается как NULL
.nIDEvent
- Задает идентификатор таймера, который должен быть удален. В нашем случае, когда первый параметр равен NULL
, его значение задается идентификатором, возвращенным в качестве результата по окончании работы функции SetTimer
.
Если функция успешно завершает свою работу и удаляет таймер, то в качестве результата она возвращает ненулевое значение. В случае неуспеха возвращается значение 0
.
Пример создания, работы и удаления таймера
В свое время в книге по языку Visual C++ , демонстрируя работу с таймером и соответствующими функциями Win32 API, мы разработали проект "Жизнь
". В этом проекте моделируется известная компьютерная игра, где можно задать начальную конфигурацию "жизни". Затем эта конфигурация начинает жить, изменяя свое состояние по заданным правилам. Изменение состояния происходит в качестве ответной реакции на сообщения таймера. Другим подобным примером, по существу вариацией на эту же тему, является создание экранных заставок. Сейчас мы решили обойтись более простым примером, демонстрирующим суть проблемы, но не имеющим эффектной формы. В нашем тестовом примере есть две командные кнопки Start
и Finish
. В ответ на нажатие первой кнопки создается таймер, соответствующая ему Callback
функция ведет подсчет числа ее вызов и уведомляет об этом, печатая значение счетчика в окне отладки. При нажатии кнопки Finish
таймер удаляется. Кнопки можно нажимать многократно. Все процедуры обработки помещены в модуль Таймер
. Вот его текст:
Пример 6: html, txt
Комментариев, приведенных в тексте, полагаем достаточно для понимания всех деталей. Приведем еще результаты печати, периодически появляющиеся в окне отладки. Следует только сказать, что дважды были поочередно нажаты кнопки Start
и Finish
:
Создан Таймер: Идентификатор =32578
Hi 1
Hi 2
Hi 3
Удален Таймер: Идентификатор =32578
Создан Таймер: Идентификатор =32573
Hi 1
Hi 2
Hi 3
Hi 4
Удален Таймер: Идентификатор =32573
Заметьте, в нашей реализации кнопки нужно нажимать поочередно, поскольку хранится только последнее значение идентификатора таймера, так что если подряд нажать несколько раз кнопку Start
, то будет создано несколько таймеров, но при последующих нескольких нажатиях кнопки Finish
будет удален только один, последний созданный таймер и печать в окне отладки будет продолжаться.
Назад Оглавление Вперёд