zoukankan      html  css  js  c++  java
  • Windows中使用精确计时器

    1.前言。 

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

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

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

    然而需要指出的是,由系统发给应用程序的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

  • 相关阅读:
    JavaFx在macOS下的文字渲染Bug
    Java多接口同名方法的冲突
    旧技术的惯性
    一点思考(1)
    slisp:编译到JVM平台上的lisp方言
    Arcee:又一个 Parser Generator 轮子
    使用Java实现一门简单的动态语言
    Hello World!
    [NOI2008]奥运物流
    [IOI2005]Riv河流
  • 原文地址:https://www.cnblogs.com/linyawen/p/2226905.html
Copyright © 2011-2022 走看看