使用timesetevent函数建立计时事件。该函数在delphi中的mmsystem.pas中有定义,定义如下:function timesetevent(udelay, uresolution: uint;  lpfunction: tfntimecallback; dwuser: dword; uflags: uint): mmresult; stdcall函数定义中参数udelay定义延迟时间,以毫秒为单位,该参数相当于timer控件的interval属性。参数uresolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;参数lpfunction定义了timesetevent函数的回调函数。该函数相当于一个定时中断处理函数,每当经过一个udelay长度的时间间隔,该函数就会被调用,编程者可以在该函数中加入相应的处理语句。参数dwuser定义用户自定义的回调值,该值将传递给回调函数。参数uflags定义定时类型,如果要不间断的记时,该值应设置为1。如果函数调用成功,在系统中建立了一个多媒体记时器对象,每当经过一个udelay时间后lpfunction指定的函数都会被调用。同时函数返回一个对象标识,如果不再需要记时器则必须要使用timekillevent函数删除记时器对象。 由于windows是一个多任务的操作系统,因此基于api调用的记时器的精度都会受到其它很多因素的干扰。到底这两中记时器的精度如何,我们来使用以下的程序进行验证:设置三种记时器(timer控件、高性能频率记数、多媒体记时器)。将它们的定时间隔设置为10毫秒,让它们不停工作直到达到一个比较长的时间(比如60秒),这样记时器的误差会被累计下来,然后同实际经过的时间相比较,就可以得到它们的精度。下面是具体的检测程序。unit unit1; interface uses  windows, messages, sysutils, classes, graphics, controls, forms, dialogs,  stdctrls, extctrls,mmsystem; type  tform1 = class(tform)  edit1: tedit;  edit2: tedit;  edit3: tedit;  button1: tbutton;  button2: tbutton;  timer1: ttimer;  procedure formcreate(sender: tobject);  procedure button1click(sender: tobject);  procedure timer1timer(sender: tobject);  procedure button2click(sender: tobject);  private  { private declarations }  public  { public declarations }  end; var  form1: tform1;  acttime1,acttime2:cardinal;  smmcount,stimercount,spcount:single;  htimeid:integer;  iten:integer;  protimecallback:tfntimecallback; procedure timeproc(utimerid, umessage: uint;  dwuser, dw1, dw2: dword) stdcall;procedure proendcount;implementation {$r *.dfm}//timesetevent的回调函数procedure proendcount;begin  acttime2:=gettickcount-acttime1;  form1.button2.enabled :=false;  form1.button1.enabled :=true;  form1.timer1.enabled :=false;  smmcount:=60;  stimercount:=60;  spcount:=-1;   timekillevent(htimeid);end; procedure timeproc(utimerid, umessage: uint;  dwuser, dw1, dw2: dword) stdcall;begin  form1.edit2.text:=floattostr(smmcount);  smmcount:=smmcount-0.01;end; procedure tform1.formcreate(sender: tobject);begin  button1.caption :='开始倒计时';  button2.caption :='结束倒计时';  button2.enabled :=false;  button1.enabled :=true;  timer1.enabled :=false;  smmcount:=60;  stimercount:=60;  spcount:=60;end; procedure tform1.button1click(sender: tobject);var  lgtick1,lgtick2,lgper:tlargeinteger;  ftemp:single;begin  button2.enabled :=true;  button1.enabled :=false;  timer1.enabled :=true;  timer1.interval :=10;  protimecallback:=timeproc;  htimeid:=timesetevent(10,0,protimecallback,1,1);  acttime1:=gettickcount;   //获得系统的高性能频率计数器在一毫秒内的震动次数  queryperformancefrequency(lgper);  ftemp:=lgper/1000;  iten:=trunc(ftemp*10);  queryperformancecounter(lgtick1);  lgtick2:=lgtick1;  spcount:=60;  while spcount>0 do begin  queryperformancecounter(lgtick2);  //如果时钟震动次数超过10毫秒的次数则刷新edit3的显示  if lgtick2 - lgtick1 > iten then begin  lgtick1 := lgtick2;  spcount := spcount - 0.01;  edit3.text := floattostr(spcount);  application.processmessages;  end;  end;end; procedure tform1.timer1timer(sender: tobject);begin  edit1.text := floattostr(stimercount);  stimercount:=stimercount-0.01;end; procedure tform1.button2click(sender: tobject);begin  proendcount;  //显示从开始记数到记数实际经过的时间  showmessage('实际经过时间'+inttostr(acttime2)+'毫秒');end; end.  运行程序,点击“开始倒记时”按钮,程序开始60秒倒记时,由于上面的程序只涉及了记时器程序的原理而没有将错误处理加入其中,所以不要等60秒倒记时结束。点击“结束倒记时”按钮可以结束倒记时。这时在弹出对话框中会显示实际经过的时间(单位为毫秒),将三个文本框内的时间乘以1000再加上实际经过的时间,越接近60000,则记时精度越高。

解决方案 »

  1.   

    一旦实现了高精度定时器,便可满足各种各样的定时要求。在使
    用时应注意,当定时间隔很短时,应保证能及时响应定时消息,否则有
    可能丢失定时消息,使定时不准。
    procedure TForm1.Button1Click(Sender: TObject);
    var
      li : TLARGEINTEGER;
    begin
      QueryPerformanceFrequency(li);
      ShowMessage(FloatToStr(Comp(li)));
      QueryPerformanceCounter(li);
      ShowMessage(FloatToStr(Comp(li)));
      QueryPerformanceCounter(li);
      ShowMessage(FloatToStr(Comp(li)));
    end;
    /////////////////////////////////////////////////
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
    Dialogs,
      StdCtrls, Numedit, ExtCtrls;type
      TForm1 = class(TForm)
        StartButton: TButton;
        StopButton: TButton;
        Edit1: TEdit;
        Label1: TLabel;
        procedure StartButtonClick(Sender: TObject);
        procedure StopButtonClick(Sender: TObject);
      private
        { Private-Deklarationen}
      public
        { Public-Deklarationen}
      end;var
      Form1: TForm1;
      x1, x2, Time: Longint;
      stopped: boolean;
    implementation{$R *.DFM}procedure TForm1.StartButtonClick(Sender: TObject);
    var x: integer;
    begin
    Time:=0;
    Stopped:=false;
      asm
      mov  al, 01110100b
      out  43h, al
      mov  al, 0A9h
      out  41h, al
      mov  al, 04h
      out  41h, al
      end;
    repeat
      asm
      xor  eax, eax
      mov  al, 01000100b
      out  43h, al
      in   al, 41h
      mov  ah, al
      in   al, 41h
      xchg ah, al
      mov  x1, eax
      end;
    If x1>x2 then Begin
      Inc (Time);  
      Application.ProcessMessages;
      Edit1.Text:=Inttostr(Time);
      end;
    x2:=x1;
    {inc(c);
    D[c]:=Word(x1);}
    until stopped;
    end;procedure TForm1.StopButtonClick(Sender: TObject);
    begin
    stopped:=true;
    end;initialization
      x1:=0;
      Time:=0;
      x2:=10000;
      c:=0;
      stopped:=false;
    end.
      

  2.   

    以下定时器的精度为1毫秒。该程序的内容十分简单:按下按钮1,启动定时器,在Edit框中显示定时值(计数值),按下按钮2则将关闭定时器。此程序在Windows95环境下,采用C++Builder3.0编程并编译通过,但设计思想同样适用于其它高级语言。按照本程序中的实现方法,可以轻松实现自己的高精度定时器。
    ---- 程序清单如下:
    #include < vcl.h >#pragma hdrstop#include "mmsystem.h"   //包含多媒体定时器函数的头文件#define MilliSecond  1  //定时间隔1毫秒#define Accuracy     1  //系统允许的分辨率最小值#define Min(x,y)  ((x < y) ? x : y)#define Max(x,y)  ((x > y) ? x : y)#include "HighTimerU.h"//------------------------------------------------#pragma package(smart_init)#pragma resource "*.dfm"UINT TimerID;    //定义定时器句柄int count;       //定义一个变量以进行计数int TimerAccuracy;TForm1 *Form1;//------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)    : TForm(Owner){}void PASCAL TimerCallProc(UINT  TimerID, UINT msg,DWORD dwUser,           DWORD dwa,DWORD dwb)  //定义定时器事件的调用函数{    count++;    Form1- >Edit1->Text=count;    //在一个编辑框内显示计数值,即定时值}//---------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender){    TIMECAPS timecaps;    int TimerResolution; //从系统获得关于定时器服务能力的信息,//分辨率不能超出系统许可值(1到16毫秒)  if (timeGetDevCaps(&timecaps,sizeof(TIMECAPS))==TIMERR_NOERROR)      TimerAccuracy=Min(Max(timecaps.wPeriodMin,Accuracy),timecaps.wPeriodMax);    timeBeginPeriod(TimerAccuracy);  //设置定时器分辨率      TimerResolution=1;    //设置定时间隔为1毫秒   //产生间隔1毫秒,周期执行的定时器事件;启动定时器    TimerID = timeSetEvent(TimerResolution,TimerAccuracy,                        &TimerCallProc,1,TIME_PERIODIC);}//-------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender){    timeKillEvent(TimerID);        //删除定时器事件    timeEndPeriod(TimerAccuracy);  //清除定时器分辨率}
    ---- 多媒体定时器对实现高精度定时是很理想的工具,而且其精度是十分可靠的。但是,多媒体定时器也并不是完美的。因为它可靠的精度是建立在对系统资源的消耗之上的。因此,在利用多媒体定时器完成工作的同时,必须注意以下几点:
    ---- 1. 多媒体定时器的设置分辨率不能超出系统许可范围。
    ---- 2. 在使用完定时器以后,一定要及时删除定时器及其分辨率,否则系统会越来越慢。
    ---- 3. 多媒体定时器在启动时,将自动开辟一个独立的线程。在定时器线程结束之前,注意一定不能再次启动该定时器,不然将迅速造成死机。