Delphi.Заметки программиста
f65d50f6

Начальное время цикла Counter: Integer;


unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;
type
TfmExample = class(TForm) Panel1: TPanel; bbRun: TBitBtn; bbClose: TBitBtn; edInput: TEdit; lbOutput: TLabel; mmOutput: TMemo; Timer1: TTimer; procedure bbRunClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
BegTime: TDateTime; // Начальное время цикла Counter: Integer; // Счетчик цикла end;
var
fmExample: TfmExample;
implementation
{$R *.DFM}
procedure TfmExample.bbRunClick(Sender: TObject);
// Запускает таймер. edInput содержит период его срабатывания. var
Delay: Word; begin


// Проверяем задание интервала if edInput.Text='' then Exit; try
Delay := StrToInt(edInput.Text);
except
ShowMessage('Ошибка в записи числа');
edInput.SelectAll; edInput.SetFocus; Exit end;
Counter := 0; // Сбрасываем счетчик Timer1.Interval := Delay; // Устанавливаем интервал BegTime := Time; // Засекаем время Timer1.Enabled := True; // Пускаем таймер Screen.Cursor := crHourGlass end;
procedure TfmExample.Timer1Timer(Sender: TObject);
var
h, m, s, ms: Word; // Переменные для декодирования времени const
MaxCount = 55; // Количество срабатываний таймера begin
Counter := Counter + 1; // Наращиваем счетчик срабатываний if Counter=MaxCount then // Конец цикла? begin // - Да Timer1.Enabled := False; // Останавливаем таймер // Находим среднее время срабатывания: DecodeTime((Time-BegTime)/MaxCount, h, m, s, ms);
mmOutput.Lines.Add( // Выводим результат Format('Задано %s ms. Получено %d ms.', [edInput.Text, ms]));
edInput.Text := ''; // Готовим следующий запуск edInput.SetFocus; Screen.Cursor := crDefault end;
end;
procedure TfmExample.FormActivate(Sender: TObject);
begin
edInput.SetFocus end;
end.
Необходимость нескольких (MaxCount) срабатываний для точного усреднения результата связана с тем, что системные часы обновляются каждые 55 мс. После запуска программы и ввода 1 как требуемого периода срабатывания в редакторе mmOutput вы увидите строку


Задано 1 ms. Получено 55 ms.
в которой указывается, какое реальное время разделяет два соседних события OnTimer. Если вы установите период таймера в диапазоне от 56 до 110 мс, в строке будет указано 110 ms и т.д. (в силу дискретности обновления системных часов результаты могут несколько отличаться в ту или иную сторону).
В ряде практически важных областей применения (при разработке игр, в системах реального времени для управления внешними устройствам и т.п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше, однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции API.
Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с заголовком:
procedure TimeProc(uID, uMsg: UINT; dwUser, dw1, dw2: DWORD);
stdcall;
Здесь uID — идентификатор события таймера (см. об этом ниже);
uMsg — не используется; dwUser — произвольное число, передаваемое процедуре в момент срабатывания таймера; dw1, dw2 — не используются.
Запуск таймера реализуется функцией:
function timeSetEvent(uDelay, uResolution: UINT; lpTimeProc: Pointer; dwUser: DWORD; fuEvent: UINT): UINT; stdcall; external 'winmm.dll';
Здесь uDelay — необходимый период срабатывания таймера (в мс);
uResolution — разрешение таймера (значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение);
lpTimeProc — адрес процедуры обратного вызова; dwUser — произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuEvent — параметр, управляющий периодичностью возникновения события таймера: TIME_ONESHOT (0) — событие возникает только один раз через uDelay миллисекунд; TIME_PERIODIC (1) — события возникают периодически каждые uDelay мс. При успешном обращении функция возвращает идентификатор события таймера и 0, если обращение было ошибочным.
Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией:
function timeKillEvent(uID: UINT): UINT; stdcall; external 'winmm.dll';
Здесь uID — идентификатор события таймера, полученный с помощью timeSetEvent.
В следующем примере (Timer.dpr) иллюстрируется использование мультимедийного таймера (листинг 15).

Содержание раздела