Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 7:

WinApi

Функции 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 таймер удаляется. Кнопки можно нажимать многократно. Все процедуры обработки помещены в модуль Таймер. Вот его текст:

'Функции работы с таймером
Public Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, _
		ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long

Public Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long

'Глобальная информация
Public Counter As Long	'Счетчик числа вызовов Callback функции
Public IdEv As Long

Public Sub HowManyProc(ByVal HandleW As Long, ByVal msg As Long, _
			ByVal idEvent As Long, ByVal TimeSys As Long)
		'Функция обратного вызова. Вызывается при обработке сообщения WM_Timer,
		'посылаемого таймером, созданным процедурой SetTimer
		
		Counter = Counter + 1
		Debug.Print "Hi", Counter
		
	End Sub

Public Sub Start()
		
		'Создает таймер, вызывая Win32 Api функцию SetTimer
		Counter = 0
		IdEv = SetTimer(0&, 0&, 10000, AddressOf HowManyProc)
		If IdEv = 0 Then
			MsgBox ("Не удалось создать таймер!")
		Else
			Debug.Print "Создан Таймер: Идентификатор = ", IdEv
		End If
 
End Sub

Public Sub Finish()
		'Удаляет таймер
		If IdEv > 0 Then
			Call KillTimer(0&, IdEv)
			Debug.Print "Удален Таймер: Идентификатор = ", IdEv
			IdEv = 0
		End If
		
End Sub
6.11.

Комментариев, приведенных в тексте, полагаем достаточно для понимания всех деталей. Приведем еще результаты печати, периодически появляющиеся в окне отладки. Следует только сказать, что дважды были поочередно нажаты кнопки Start и Finish:

Создан Таймер: Идентификатор =32578 
Hi					1 
Hi					2 
Hi					3 
Удален Таймер: Идентификатор =32578 
Создан Таймер: Идентификатор =32573 
Hi					1 
Hi					2 
Hi					3 
Hi					4 
Удален Таймер: Идентификатор =32573

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

полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.