[轉貼]在Windows中使用精確的計時器

有關於C/C++的語法, 程式等
回覆文章
頭像
tim
文章: 1380
註冊時間: 2008年 11月 26日, 00:49

[轉貼]在Windows中使用精確的計時器

文章 tim »

原文出處
http://red2008.xiloo.com/index3/a2.htm


在Windows中使用精确计时器

 

本文讨论了Windows中精确计时器的实现方法,并分析了Windows的两个相关的未

公开函数,最后给出了一个使用精确计时器的例子。

关键词:系统计时器,回调函数。

 

1.前言。

我们知道,在Windows中,可以通过调用SetTimer函数为应用程序分配一个计时器。当

指定了一个时间间隔以后,Windows系统将每隔指定的时间向应用发送一条WM_TIMER消

息,从而使应用程序能够实现许多与时间相关的动作。

步的,这条消息被

放在常规的消息队列中,并与其它消息一起排序。因此,即使我们在调用SetTimer()

时设定了1000毫秒的时间间隔,应用程序却不一定保证每隔一秒钟接受到一条

WM_TIMER消息,如果另一个程序的忙碌时间超过一秒钟,那么我们的应用程序在那段

时间内就不能接收到任何WM_TIMER消息。

显然,这种情况的存在对那些需要精确时间间隔的应用(如某些监控程序)来说是致命

的。所幸的是,在Windows中隐藏着某些机制,使得我们能够获得精确计时器服务。

 

2.系统计时器.

在Windows的SYSTEM.DRV驱动程序中提供了几个鲜为人知的系统计时器函数(这几个函

数未写入Windows.h中,但却被SYSTEM.DRV输出了),这几个函数可以帮助我们获得精

确计时器服务,即系统计时器。这其中最重要的是CreateSystemTimer()和

KillSystemTimer(),这两个函数允许我们安装异步计时器的回调函数(Callback),有

些类似于在DOS环境中截取INT 8中断处理程序。这个回调是真正异步的,完全避开了

Windows的消息工具,因而具有重要意义。事实上,Microsoft Excel和Windows COMM

驱动程序都用到了系统计时器,而由SetTimer()安装的一般计时器也是由系统计时器

来实现的。

这两个函数的原型如下:

WORD CreateSystemTimer(wMsecInterval,lpfnTimerProc);

WORD wMsecInterval; /*以毫秒为单位的时间间隔,系统将每隔此时间调用一 次回调函数

*/

FARPROC lpfn TimerProc;/*指向回调函数的指针*/

WORD KillSystemTimer(hTimer);

WORD hTimer;/*欲释放的系统计时器句柄*/

 

其中,CreateSystemTimer()用于安装一个系统计时器回调函数,SYSTEM INT8处理程序将

按wMsecInterval指定的时时间间隔调用此回调函数。当然,这个指定的回调频率也是有

限的,同SetTimer()一样,每秒钟调用回调函数次数不能超过18.2次,即wMsecInterval>

55。该函数返回一个系统计时器句柄。若安装失败,则返回NULL。KillSystemTimer()则

用于撤销一个已安装的系统计时器hTimer。若成功,则返回;出错则返回传给它的参数

hTimer。

 

3.使用系统计时器应注意的问题。

系统计时器回调函数虽然不是中断处理程序,但由于它直接被中断处理程序调用,因此也

必将它看作中断代码。这也就决定了在使用过程中必须注意以下几个问题:

(1).在回函数中应包括尽量少的代码,以使得频繁回调的该函数不至于占用太多的CPU时

间。一般情况下,系统计时器总是用来监视或设置某些变量的值。

(2).由于该回调函数属于中断代码,因此大多数Windows API函数调用都不适用了,只有

几个简单的函数仍然可以使用,如PostMessage(),GetCurrentTask()和MessageBeep()

等。

(3).由于该回调函数由中断处理程序直接调用,因此该函数必须放在一个固定的代码段

中,并且调用前必须装载DS寄存器,这可由形实替换函数MakeProcInstance()来做到。

另外,由于这两个函数在Windows.h中没有给出(即Windows缺省输入库不含此两函数),因

此在调用之前必须进行链接。这可采用运行时动态链接,即通过GetModuleHandle()和

GetProcAddress()来链接;也可在程序模块定义文件中用IMPORTS语句来引入,此时则必

须在程序源文件中说明CreateSystemTimer()和KillSystemTimer()为外部函数。本文给出

的例子采用第二种方案。

 

4.一个例子。

本文最后给出一个简单的例子,以说明系统计时器是如何工作的。

在本例中,我们安装了一个每秒钟调用一次的回调函数,该回调函数发出一声蜂鸣。为了

测试该系统计时器,我们特意编写了一段较长时间的循环语句。在这段循环中,由

SetTimer()

安装的通常计时器是不能工作的(因为Windows是一个非抢先的系统),而我们安装的系统

计时器仍然能每隔一秒钟发出一声蜂鸣。

该例子在MSVC++1.5中调试通过,运行良好。

//SystemT.c

#include<windows.h>

 

extern WORD WINAPI CreateSystemTimer(WORD wTimeOut,FARPROC lpfnTimerProc);

extern WORD WINAPI KillSystemTimer(WORD hTimer);

 

void FAR PASCAL_export MyTimerProc(void);

WORD SetUpSystemTimer(WORD wTimeOut);

BOOL ClearSystem Timer(WORD hTm);

 

FARPROC fpTimerProc=NULL;

WORD hTimer=NULL;

char szAppName[]="SystemTimer";

 

int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,

LPSTR lpszCmdParam,int nCmdShow)

{

WNDCLASS wc;

HWND hWndMain;

int i,j;

HCURSOR hcurSave;

 

if(hPrevInstance=NULL){

wc.lpszMenuName =NULL; wc.lpszClassName =szAppName; wc.hInstance =hInstance; wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor =LoadCursor(NULL,IDC_ARROW);

wc.hbrBackground =(HBRUSH)COLOR_WINDOW+1;

wc.style =0; wc.lpfnWndproc =DefWindowProc;

wc.cbClsExtra =0; wc.cbWndExtra =0;

if(!RegisterClass(&wc))

return(0);

}

if(hWndMain=CreateWindow(szAppName,

szAppName,WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,CW_USEDEFAULT,

CW_USEDEFAULT,CW_USEDEFAULT,

NULL,NULL,hInstance,NULL))=NULL)

return(0);

ShowWindow(hWndmain,nCmdShow);

Update Window(hWndMain);

 

fpTimerProc=MakeProcInstance((FARPROC)MyTimerProc,hInstance);

if(hTimer=SetUpSystemTimer(1000))=NULL{

MessageBox(hwndMain,"Set System Timer Error",

szAppName,MB_ICONEXCLAMATION:MB_OK);

return 0;

}

hcurSave=SetCursor(LoadCursor(NULL,IDC_WAIT));

for(i=0;i<10000;i++)

for(j=0;j<10000;j++)

SetCursor(hcurSave);

ClearSystemTime(hTimer);

}

WORD SetUpSystemTime(WORD wTimeOut)

{

WORD hTm;

if((hTm=CreateSystemTimer(wTimeOut,fpTimerProc))=NULL){

fpTimerProc=NULL:

return NULL

}else return hTm;

}

BOOL ClearSystemTimer(WORD hTm)

{

if(hTm){

if(KillSystemTimer(hTm)!=0)

return FALSE;

hTm=NULL;

}

return TRUE

voidFAR PASCAL_export MyTimerProc(void)

{

MessageBeep(0);

}

////////////////////////////////////

//SystemT.def

NAME SystemTimer

DESCRIPTION'System Timer'

EXETYPE WINDOWS

STUB 'WINSTUB'

CODE PRELOAD

DATA PRELOAD MOVABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS MyTimerProc

IMPORTS CreateSystemTimer=SYSTEM.CREATESYSTEMTIMER

KillSystemTimer=SYSTEM.KILLSYSTEMTIMER
多多留言, 整理文章, 把經驗累積下來.....
回覆文章