Click here to Skip to main content
15,902,788 members
Articles / Desktop Programming / MFC
Article

Timers in MFC / C++

Rate me:
Please Sign up or sign in to vote.
4.20/5 (5 votes)
17 Jan 2006 93.2K   39   18
Implementing one-shot timers.

Introduction

Setting a timer in C++/Win32 that fires once, normally requires a lot of work, considering that all you really want to do is wait a bit and execute a function, but not block mainline code execution. This simple class makes OneShot timers easy. No more struggling with static functions and 'this' pointers.

Using the code

The only special instructions for using this class are:

  • You must instantiate the timer object using new, because the object deletes itself after the timer fires.
  • The target function is called using PostMessage, so there must be a MESSAGE_MAP entry (either ON_MESSAGE, or ON_COMMAND) for the target function.
// Map the function you want to call.
// You can use either Command or Message format:
BEGIN_MESSAGE_MAP(CMyClass, CWnd)
       ON_COMMAND( IDC_DOCOMMAND, OnDoCommand )
    ON_MESSAGE( IDC_DOMESSAGE, OnDoMessage )
END_MESSAGE_MAP()
// Your Functions
void CMyClass::OnDoCommand()
{
   return;
}

LRESULT CMyClass::OnDoMessage( WPARAM wParam, 
                               LPARAM lParam )
{
   return 0;
}
// In your code, fire the OneShots. Remember they must use new, as 
//they are self deleting once the timer fires.
//The TimerID is guaranteed to be unique as the object 
//exists for the the duration of the timer.

new CTimerOneShot( 2000, GetSafeHwnd(), 
                   IDC_DOCOMMAND ); // Command Version

// or

new CTimerOneShot( 2000, GetSafeHwnd(), 
                   IDC_DOMESSAGE, 123, 456 ); // Message Version
// And here is the OneShot class. 
#pragma once

class CTimerOneShot
{
public:

   // MESSAGE Format
   CTimerOneShot( int a_msecs, HWND a_targetHwnd, 
                  int a_messageID, WPARAM a_wparam, 
                  LPARAM a_lparam )
   {
      m_timerID = (UINT) this;
      m_targetHwnd = a_targetHwnd;
      m_messageID = a_messageID;
      m_lparam = a_lparam;
      m_wparam = a_wparam;
      m_msecs = a_msecs;

      ::SetTimer( a_targetHwnd, m_timerID, 
                  m_msecs, TimerProcA );
   }

   static void CALLBACK EXPORT TimerProcA( HWND a_hwnd, 
          UINT /*WM_TIMER*/, 
          UINT a_timerID, DWORD a_systemtime )
   {
      CTimerOneShot* pOneShot = (CTimerOneShot*) a_timerID;
      ASSERT( a_hwnd == pOneShot->m_targetHwnd );
      ::KillTimer( pOneShot->m_targetHwnd, a_timerID );
      ::PostMessage( pOneShot->m_targetHwnd, 
                     pOneShot->m_messageID, 
                     pOneShot->m_wparam, 
                     pOneShot->m_lparam );
      delete pOneShot;
   }

   // COMMAND Format
   CTimerOneShot( int a_msecs, 
      HWND a_targetHwnd, int a_commandID )
   {
      m_timerID = (UINT) this;
      m_targetHwnd = a_targetHwnd;
      m_commandID = a_commandID;
      m_msecs = a_msecs;

      ::SetTimer( a_targetHwnd, m_timerID, 
                  m_msecs, TimerProcB );
   }

   static void CALLBACK EXPORT TimerProcB( HWND a_hwnd, 
          UINT /*WM_TIMER*/, UINT a_timerID, 
          DWORD a_systemtime )
   {
      CTimerOneShot* pOneShot = (CTimerOneShot*) a_timerID;
      ASSERT( a_hwnd == pOneShot->m_targetHwnd );
      ::KillTimer( pOneShot->m_targetHwnd, a_timerID );
      ::PostMessage( pOneShot->m_targetHwnd, 
                     WM_COMMAND, pOneShot->m_commandID, 0 );
      delete pOneShot;
   }

   HWND   m_targetHwnd;
   int    m_messageID;
   int    m_commandID;
   LPARAM m_lparam;
   WPARAM m_wparam;
   UINT   m_timerID;
   int    m_msecs;
};

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPorting code to 64-bit Pin
dimag21-Aug-14 10:11
dimag21-Aug-14 10:11 
Generalmyapp interface Pin
giannib2k20-May-07 23:49
giannib2k20-May-07 23:49 
GeneralRe: myapp interface Pin
Steve Johnson (Sven)21-May-07 4:01
Steve Johnson (Sven)21-May-07 4:01 
GeneralExcellent - Very Handy Pin
Yogesh Dhakad22-Apr-07 1:19
Yogesh Dhakad22-Apr-07 1:19 
GeneralThanks for the Info Pin
lanrosta20-Jan-06 10:40
lanrosta20-Jan-06 10:40 
GeneralRe: Thanks for the Info Pin
Rob Rippengale24-Jan-06 18:33
Rob Rippengale24-Jan-06 18:33 
GeneralRe: Thanks for the Info Pin
lanrosta25-Jan-06 11:51
lanrosta25-Jan-06 11:51 
GeneralRe: Thanks for the Info Pin
Rob Rippengale26-Jan-06 11:44
Rob Rippengale26-Jan-06 11:44 
GeneralRe: Thanks for the Info Pin
Steve Johnson (Sven)26-Jan-06 11:54
Steve Johnson (Sven)26-Jan-06 11:54 
GeneralRe: Thanks for the Info Pin
Rob Rippengale28-Jan-06 14:16
Rob Rippengale28-Jan-06 14:16 
With multiple timers trying to access the same global object, potentially at the same time, I agree it would probably be much safer to work out a specialized one-shot object for the timer to call and destroy, and your example has much to offer. Thanks for sharing this.

I don't know what Ianrosta is trying to do, but it sounded like he needed some clarification for the underlying timer callback scheme, so I offered a simplistic situation to cheer him up. It would certainly be a good exercise to go through your example and understand what's going on there, especially regarding the passing of the "this" pointer, but it seems a little complicated for someone learning C++. Good challenge though.

For my own purposes I sometimes want a timer that will frequently interact with a long-term object already in existence, in a context where I expect no conflict and want a fast response with minimal overhead. Invoking a new class or subclass for each of those calls seems like overkill to me, and the global pointer works fine. But I confess most of my code uses C++ as if it was "C with some objects" and apparently I don't use object-oriented methods as often as you might.

Interesting example. Thanks for the pointers. Wink | ;)

GeneralRe: Thanks for the Info Pin
Steve Johnson (Sven)28-Jan-06 16:48
Steve Johnson (Sven)28-Jan-06 16:48 
GeneralI wouldn't use WM_TIMER Pin
#realJSOP17-Jan-06 9:41
professional#realJSOP17-Jan-06 9:41 
GeneralRe: I wouldn't use WM_TIMER Pin
Steve Johnson (Sven)17-Jan-06 9:56
Steve Johnson (Sven)17-Jan-06 9:56 
GeneralRe: I wouldn't use WM_TIMER Pin
#realJSOP18-Jan-06 3:23
professional#realJSOP18-Jan-06 3:23 
GeneralRe: I wouldn't use WM_TIMER Pin
Steve Johnson (Sven)18-Jan-06 4:44
Steve Johnson (Sven)18-Jan-06 4:44 
QuestionRe: I wouldn't use WM_TIMER Pin
Chris Hills18-Mar-06 5:05
Chris Hills18-Mar-06 5:05 
AnswerRe: I wouldn't use WM_TIMER Pin
Steve Johnson (Sven)18-Mar-06 7:04
Steve Johnson (Sven)18-Mar-06 7:04 
GeneralRe: I wouldn't use WM_TIMER Pin
Rasqual Twilight17-Aug-06 3:28
Rasqual Twilight17-Aug-06 3:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.