zoukankan      html  css  js  c++  java
  • Windows定时器

    1定时器    1

    1.1 创建定时器    1

    1.2 销毁定时器    1

    1.3 定时器的运作    1

    1.3.1 产生WM_TIMER消息    1

    1.3.2 分发WM_TIMER消息    2

    1.4 WM_TIMER 消息的重入    3

    1定时器

    1.1 创建定时器

    请使用API函数 SetTimer 来创建定时器,其原型如下:

    UINT SetTimer(HWND hWnd,UINT nIDEvent,UINT uElapse,TIMERPROC lpTimerFunc);

    有这么两种用法

    1SetTimer(hWnd,nID,uElapse,NULL);定时给窗口 hWnd 寄送(PostMessage) WM_TIMER 消息;

    2SetTimer(hWnd,nID,uElapse,TimerProc); 不论 hWnd 是否为 NULL,定时调用 TimerProc 函数。

    1.2 销毁定时器

    销毁定时器请使用KillTimer函数,其原型如下:

    BOOL KillTimer(HWND hWnd,UINT uIDEvent);

    1个参数应与SetTimer的第1个参数保持一致;

    2个参数:如果SetTimer的第1个参数是一个有效的窗口句柄,则此参数应与SetTimer的第2个参数保持一致。否则此参数应为SetTimer的返回值。

    1.3 定时器的运作

    不论 SetTimer(hWnd,nID,uElapse,NULL) 还是 SetTimer(NULL,nID,uElapse,TimerProc),其实质都是处理WM_TIMER消息。

    1.3.1 产生WM_TIMER消息

    WM_TIMER消息并不是 Windows 系统定时、自动增加到消息队列的,而是调用GetMessagePeekMessage的时候,才会产生WM_TIMER消息。请参考如下代码:

    void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)

    {

    }

     

    UINT TestTimer()

    {

    MSG msg;

    UINT nTimer = SetTimer(NULL,100,1000,TimerProc);

     

    Sleep(3050);

    TRACE(_T("Tick=%d "),GetTickCount());

    PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);

    Sleep(1050);

    TRACE(_T("Tick=%d "),GetTickCount());

    PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);

     

    while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))

    {

    if(msg.message == WM_TIMER)

    {

    TRACE(_T("Timer=%d "),msg.time);

    }

    }

    KillTimer(NULL,nTimer);

    return 0;

    }

    Windows XP下,运行结果为:

    Tick=7356593

    Tick=7357656

    Timer=7356593

    Timer=7357656

    虽然两次Sleep的时间合计有4秒多,但消息队列中WM_TIMER的个数并不是4个而是2个。而且这两个WM_TIMER的时刻与两次GetTickCount的时刻完全相等。合理的解释是:在调用PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);时,WM_TIMER消息才被创建并增加到消息队列。如果调用GetMessagePeekMessage(&msg,NULL,0,0,PM_REMOVE);则创建的WM_TIMER消息不会被增加到消息队列。

    1.3.2 分发WM_TIMER消息

    通过GetMessagePeekMessage获得消息之后,一般会调用TranslateMessageDispatchMessage 进行消息处理。

    TranslateMessage WM_TIMER 消息不做任何处理。

    DispatchMessage(&msg) 负责分发 WM_TIMER 消息,其处理逻辑如下:

    if(msg.lParam)

    {//如果SetTimer的第4个参数不为NULL,则调用这个回调函数

    TIMERPROC pfn = (TIMERPROC)msg.lParam;

    pfn(msg.hwnd,WM_TIMER,msg.wParam,msg.time);

    }

    else

    {//交给窗口过程去处理

    WNDPROC pfn = (WNDPROC)GetWindowLong(msg.hwnd,GWL_WNDPROC);

    CallWindowProc(pfn,msg.hwnd,WM_TIMER,msg.wParam,msg.lParam);

    }

    也就是说:如果SetTimer的第4个参数不为 NULL,则第1个参数所指定的 hwnd 将无法接收、处理 WM_TIMER 消息。

    1.4 WM_TIMER 消息的重入

    所谓重入就是当前的消息还没有处理完毕就进入下一个消息的处理。因为WM_TIMER消息是入队消息,所以一般情况下,对WM_TIMER的处理是不会重入的。但也有特殊情况,请参考如下代码:

    //定义定时器处理函数

    void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)

    {

    TRACE(_T("进入 OnTimer=%lu "),dwTime);

    Sleep(3500);

    MSG msg;

    while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))

    {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

    }

    Sleep(1000);

    TRACE(_T("离开 OnTimer=%lu "),dwTime);

    }

    //设置定时器

    SetTimer(100,1000,TimerProc);

    本来TimerProc一秒被调用一次,现在情况发生了变化:在TimerProc内部,Sleep(3500)后再调用PeekMessage会立即产生WM_TIMER消息。DispatchMessage会再次调用TimerProc函数处理这个消息。结果就是TimerProc函数无限制的递归调用自己,永远不会返回,最终会因为栈空间溢出而导致程序异常退出。

    为了防止TimerProc函数的重入并可能引起的程序崩溃,就需要阻止重入TimerProc函数。可行的方法之一如下:

    void CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)

    {

    static bool bWorking = false; //是否正在处理 WM_TIMER 消息

    if(bWorking)

    {//如果正在处理 WM_TIMER 消息则返回,这样就防止了重入

    return;

    }

    bWorking = true;        //标记正在处理 WM_TIMER 消息

    ... ... ...            //处理 WM_TIMER 消息

    bWorking = false;        //标记 WM_TIMER 消息已经处理完毕

    }

  • 相关阅读:
    Flink中的广播流之BroadcastStream
    啊。。这是为什么。。 花甜的工作笔记
    我那庞大身躯,脆弱心灵的3250 花甜的工作笔记
    好吧,我承认,我不是一个专一的人。。。 花甜的工作笔记
    沾沾自喜 花甜的工作笔记
    我高调的来啦。。。。 花甜的工作笔记
    今天偶听一词云终端 花甜的工作笔记
    我开始出轨了。 花甜的工作笔记
    软件限制策略。。。。。辛苦中。。 花甜的工作笔记
    推荐系统建构精选文章
  • 原文地址:https://www.cnblogs.com/hanford/p/6163706.html
Copyright © 2011-2022 走看看