定时器
一、精确计时 BCB的定时器控件Timer计时精度为55ms,且定时器的消息在多任务操作系统中的优先级很低，不能得到及时响应，往往不能满足实时控制环境下的应用。不过微软公司在 Win32 API函数库中已经为用户提供了一组用于高精度计时的底层函数，如果用户使用得当，计时精度可到1ms。这个计时精度，对于一般的实时系统控制完全可以满足要求。现将由BCB提供的重新封装后的一组与时间相关的主要接口函数（函数名、参数、功能与Win32 API基本相同）说明如下： 1.DWORD timeGetTime(void) 返回从Windows启动开始经过的毫秒数。最大值为2的32次方，约49.71天。 2.MMRESULT timeSetEvent(UINT uDelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD dwUser,UINTfuEvent) 该函数设置一个定时回调事件，此事件可以是一个一次性事件或周期性事件。事件一旦被激活，便调用指定的回调函数，成功后返回事件的标识代码，否则返回NULL.参数说明如下： uDelay:以毫秒制定事件的周期。 UResolution:以毫秒指定延时的精度，数值越小定时器事件分辩率越高。缺省值为1ms. LpTimeProc:指向一个回调函数。 DwUser:存放用户提供的回调数据。 FuEvent:指定定时器事件类型：TIME_ONESHOT:uDelay毫秒后只产生一次事件。 TIME_PERIODIC:每隔uDelay毫秒周期性地产生事件。 3.MMRESULT timeKillEvent(UINT uTimerID) 该函数取消一个指定的定时器回调事件。uTimerID标识要取消的事件（由timeSetEvent函数返回的标识符）。如果定时器时间不存在则返回MMSYSERR_INVALPARAM。 void CALLBACK TimeProc(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2); 该函数是一个应用程序定义的回调函数，出现定时器事件时该函数被调用。TimeProc是应用程序定义的函数名的占位符。使用该函数时要注意的是，它只能调用以下有限的几组API函数：PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, OutputDebugString。同时也不要使用完成时间很长的API函数，程序尽可能简短。 使用以上一组函数就可以完成毫秒级精度的计时和控制（在BCB使用时要将头文件mmsystem.h加到程序中）。由于将定时控制精确到几毫秒，定时器事件将占用大量的CPU时间和系统资源，所以在满足控制要求的前提下，应尽量将参数uResolution的数值增大。而且定时器实时控制功能完成后要尽快释放。 二、Windows 95 中 的 定 时 器 的 应 用 （李 振） 在windows95 编 程 中 有 时 要 用 到 定 时 器 的功 能， 大 家 常 用 的 是 通 过SetTimer 函 数 设 置 一 个 定 时 器， 在事 件WM_TIMER 响 应 函 数 中 处 理， 然 后 用KillTimer 函 数 取 消 此 定时 器。 但 此 种 方 式 的 定 时 器 只 能 精 确 到 大 约55 毫 秒， 对 于55 毫 秒 以 下 的 时 间 精 度 便 无 能 为 力。 笔 者 在 这 里 向 大 家 提 供 一 个 可 以 精 确 到1 毫秒 的 定 时 器 — — 多 媒 体 定 时 器。 它 主 要 通 过 以 下 函 数 来实 现： ---- timeBeginPeriod — — 建 立 应 用 程 序 使 用 的 定 时 器 分 辨率； ---- timeEndPeriod — — 清 除 前 面 用timeBeginPeriod 函 数 建 立 的最 小 定 时 器 分 辨 率； ---- timeSetEvent — — 产 生 一 个 在 指 定 的 时 间 或 时 间 周 期间 隔 内 执 行 的 定 时 器 事 件； ---- timeKillEvent — — 删 除 前 面 用timeSetEvent 产 生 的 定 时 器事 件； ---- timeGetDevCaps — — 返 回 关 于 定 时 器 服 务 能 力 的 信 息。 ---- 下 面 就 结 合 一 段 程 序 来 具 体 说 明 它 的 用 法。 这 段 程序 的 主 要 功 能 是： 设 置 两 个 时 钟 定 时 器， 一 个 间 隔 是1 毫秒， 一 个 间 隔 是2 秒，每 执 行 一 次， 输 出 当 前 系 统 时 钟 值 到文 件 “cure.out”， 以 比 较 此 定 时 器 的 精 确 度。（ 此 程 序 在 中文windows95 及Microsoft VC5.0 编 译 通 过。 只 节 取 与 定 时 器 有 关的 部 分 程 序。） #include < stdio.h > //包含所用系统函数的头文件， 如果编译有问题，可调整此语句的位置 #include < mmsystem.h > //定义1毫秒和2秒时钟间隔，以毫秒为单位 #define ONE_MILLI_SECOND1 #define TWO_SECOND 2000 //定义时钟分辨率，以毫秒为单位 #define TIMER_ACCURACY1 UINT wTimerRes_1ms,wTimerRes_2s; //定义时间间隔 UINT wAccuracy;//定义分辨率 UINT TimerID_1ms,TimerID_2s;//定义定时器句柄 CCureApp::CCureApp(): fout("cure.out", ios::out)//打开输出文件“cure.out” { // TODO: add construction code here, // Place all significant initialization in InitInstance //给时间间隔变量赋值 wTimerRes_1ms = ONE_MILLI_SECOND ; wTimerRes_2s = TWO_SECOND; TIMECAPS tc; //通过函数timeGetDevCaps取出系统的分辨率取值范围（对intel系统， 1~16毫秒），//如果无错则继续 if(timeGetDevCaps(&tc, sizeof(TIMECAPS))== TIMERR_NOERROR) { //分辨率的值不能超出系统的取值范围 wAccuracy = min(max(tc.wPeriodMin, TIMER_ACCURACY),tc.wPeriodMax); //调用timeBeginPeriod函数设置定时器的分辨率，类似于for循环的步长 timeBeginPeriod(wAccuracy); //设置定时器 InitializeTimer(); } } CCureApp::~CCureApp() { //结束时钟 fout < < "结束时钟" < < endl; //删除两个定时器 timeKillEvent(TimerID_1ms); timeKillEvent(TimerID_2s); //删除设置的分辨率 timeEndPeriod(wAccuracy); } 注：使用完的定时器及分辨率一定要删除，否则系统会越来越慢。 void CCureApp::InitializeTimer() { StartOneMilliSecondTimer(); StartTwoSecondTimer(); } //一毫秒定时器的回调函数，类似于中断处理程序 voidPASCAL//一定要声明为全局PASCAL函数，否则编译会有问题 OneMilliSecondProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2) { static int ms = 0;//定义计数器 CCureApp *app = (CCureApp *)dwUser; //取得系统时间以毫秒为单位 DWORD osBinaryTime = GetTickCount(); //输出计数器值和当前系统时间到文件 app- >fout < < ++ms < < ": 1ms : " < < osBinaryTime < < endl; } //加装1毫秒定时器 void CCureApp::StartOneMilliSecondTimer() { if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy， (LPTIMECALLBACK) OneMilliSecondProc,//回调函数 (DWORD)this,//用户自传送到回调函数的数据 /*周期调用，只使用一次用TIME_ONESHOT*/ TIME_PERIODIC)) == 0) { AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); } else//不等于0表明加装成功，返回此定时器的句柄 fout < < "16ms计时:" < < endl; } 以下为2秒定时器的回调函数和加装函数，与1毫秒的类似； void PASCAL TwoSecondProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2) { static int s = 0; CCureApp *app = (CCureApp *)dwUser; DWORD osBinaryTime = GetTickCount(); app- >fout < < "***********************" < < endl; app- >fout < < ++s < < ": 2s : " < < osBinaryTime < < endl; } void CCureApp::StartTwoSecondTimer() { if((TimerID_2s = timeSetEvent(wTimerRes_2s, wAccuracy, (LPTIMECALLBACK) TwoSecondProc, (DWORD)this, TIME_PERIODIC)) == 0) { AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); } else fout < < "2s计时:" < < endl; } 三、(网友 leex2000 关于定时器的回答） 用 Win95 的 VxD 中的VTD_Get_Real_Time 调用, 你能获得 0.8 微秒的计数.这是我所知道 Win95 能给出的最小时间间隔, Win95 的定时器是没有用的, 因为定时器消息的优先权很低, 最多到 0.5 毫秒，再低就没戏了.