fclt Logo
Timer class
Classe Timer

The timer class below is simply a wrapper around the SetTimer() API function. Its particularity is that it uses a hidden window associated with its own message pump and window procedure. This allows the class to be used in GUI applications AND in console applications. Besides, you can create multiple instances of the class since the this pointer is  hidden in the TimerID and passed to the OnTimer procedure. The class is an abstract base class. To use it, you have to derive your class from the Timer class and implement, at least, the OnTimer() method which is virtual in the abstract base class. I'll show you an example later.
La classe timer est un wrapper autour de la fonction SetTimer de l'API Windows. Elle utilise une fenêtre non-visible et sa propre pompe à messages ainsi que sa propre window procédure. Ceci permet d'utiliser la classe aussi bien dans un application graphique que console. De plus, vous pouvez créer plusieurs instances de la classe. Le pointeur this est caché dans dans le TimerID et passé à la fonction OnTimer. La classe est en fait une classe de base abstraite. Vous devez donc dériver la vôtre de cette classe en implémentant la méthode OnTimer qui est virtuelle dans la classe de base. Je vous montrerez un exemple plus bas.

#ifndef TIMER_H
#define TIMER_H
//------------------------------------------------------------
#include <windows.h>

//------------------------------------------------------------
namespace mlLib
{

class Timer
{
  private:
    static HWND hwnd_;
    unsigned int timerId_;
    bool CreateHiddenWindow();
    static LRESULT CALLBACK timerwndproc(HWND h,UINT msg,
        WPARAM wparam,LPARAM lparam);
    Timer(const Timer&);
    Timer& operator=(const Timer&);
  protected:
    virtual void OnTimer()=0;
  public:
    Timer();
    virtual ~Timer();
    bool Start();
    bool Stop();
    unsigned int Interval;  
};
//------------------------------------------------------------
//end namespace mlLib
#endif


//------------------------------------------------------------
#include "timer.h"
#include <iostream>

//------------------------------------------------------------
HWND mlLib::Timer::hwnd_=0;
mlLib::Timer::Timer()
    :timerId_(0),Interval(1000)
{
  
}

mlLib::Timer::~Timer()
{
  if(hwnd_)
  {
    Stop();
    SendMessage(hwnd_,WM_CLOSE,0,0);
  }
}

bool mlLib::Timer::CreateHiddenWindow()
{
  WNDCLASS HiddenWindowClass ;
  HiddenWindowClass.style=0;
  HiddenWindowClass.lpfnWndProc=timerwndproc;
  HiddenWindowClass.cbClsExtra=0;
  HiddenWindowClass.cbWndExtra=0;
  HiddenWindowClass.hInstance=0;
  HiddenWindowClass.hIcon=0;
  HiddenWindowClass.hCursor=0;
  HiddenWindowClass.hbrBackground=0;
  HiddenWindowClass.lpszMenuName=NULL;
  HiddenWindowClass.lpszClassName="TimerHiddenWindow";

  if(!RegisterClass(&HiddenWindowClass))return 0;
  hwnd_ = CreateWindow(HiddenWindowClass.lpszClassName, "",
       WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS |
       WS_SYSMENU | WS_MINIMIZEBOX,
       GetSystemMetrics(SM_CXSCREEN)/2,
       GetSystemMetrics(SM_CYSCREEN)/2,
       0,0,0,0,NULL,NULL);
  if(hwnd_==NULL)return false;
  HMENU   SysMenu;
  SysMenu=GetSystemMenu(hwnd_,false);
  DeleteMenu(SysMenu,SC_MAXIMIZE,MF_BYCOMMAND);
  DeleteMenu(SysMenu,SC_SIZE,MF_BYCOMMAND);
  DeleteMenu(SysMenu,SC_MOVE,MF_BYCOMMAND);
  return true;
}

LRESULT CALLBACK mlLib::Timer::timerwndproc(HWND h,UINT msg,
        WPARAM wparam,LPARAM lparam)
{
  switch(msg)
  {
    case WM_CLOSE:
      DestroyWindow(h);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    case WM_TIMER:
    {  
      Timer* ptr=reinterpret_cast<Timer*>(wparam);
      ptr->OnTimer();
      break;
    }
    default:
      return DefWindowProc(h,msg,wparam,lparam);
  }
  return 0;
}

bool mlLib::Timer::Start()
{
  if(!hwnd_)
  {
    if(!CreateHiddenWindow())return false;
  }
  // In case Start() is called multiple times
  Stop();

  timerId_=reinterpret_cast<unsigned int>(this);
  if(!SetTimer(hwnd_,timerId_,Interval,NULL))return false;
  else return true;
}

bool mlLib::Timer::Stop()
{
  if(timerId_)
  {
    if(!KillTimer(hwnd_,timerId_))return false;
    timerId_=0;
  }
  return true;
}


Here is a sample showing how to use the Timer class and create the derived classes from the abstract base class. The only requirement is to implement the OnTimer member function but you can add any member function you want of course. It's up to you.
 It's a simple window created with raw API functions. Two timers are created. Each one increments a counter and the value of that counter is shown in a static text. So basically, you'll see 2 counters; one is incrementing twice as fast as the other.
Voici un exemple montrant comment utiliser la classe Timer et dériver vos propres classes depuis la classe de base abstraite. La seule obligation est d'implémenter la fonction membre OnTimer mais vous pouvez évidemment ajouter toutes les fonctions membres que vous voulez.
C'est une simple fenêtre, créee avec les fonctions de l'API Windows. Deux timers sont créés et chacun incrémente un compteur qui est affiché dans 2 static texts. Un des deux compteurs s'incrémente deux fois plus vite que l'autre.

#include <windows.h>
#include "timer.h"

class timer1 : public mlLib::Timer
{
  private:
    HWND hwnd_;
    int count_;
  public:
    void SetTextWindow(HWND hwnd){hwnd_=hwnd;}
    void OnTimer()
    {
      char buf[16];
      itoa(count_,buf,10);
      SetWindowText(hwnd_,buf);
      ++count_;
    }
};
class timer2 : public mlLib::Timer
{
  private:
    HWND hwnd_;
    int count_;
  public:
    void SetTextWindow(HWND hwnd){hwnd_=hwnd;}
    void OnTimer()
    {
      char buf[16];
      itoa(count_,buf,10);
      SetWindowText(hwnd_,buf);
      ++count_;
    }
};


extern HINSTANCE _hInstance; 
const int TEXT1 = 1;
const int TEXT2 = 2;
//------------------------------------------------------------
LRESULT CALLBACK MainProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  static HWND text1;
  static HWND text2;
  static timer1 t1;
  static timer2 t2;
  switch (msg)
  {
    case WM_CREATE :
    {
      text1=CreateWindow("STATIC","timer 1",WS_CHILD | WS_VISIBLE,
                         10,10,150,19,hwnd,
                         (HMENU)TEXT1,_hInstance,NULL);
      text2=CreateWindow("STATIC","timer 2",WS_CHILD | WS_VISIBLE,
                         10,50,150,19,hwnd,
                         (HMENU)TEXT2,_hInstance,NULL);
      t1.SetTextWindow(text1);
      t2.SetTextWindow(text2);
      t1.Interval=1000;
      t2.Interval=500;
      t1.Start();
      t2.Start();
      return 0;
    }

    case WM_COMMAND :
      break;

    case WM_PAINT :
    {
      PAINTSTRUCT ps;
      BeginPaint(hwnd, &ps);

      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_CLOSE :
    {
      DestroyWindow(hwnd);
      break;
    }

    case WM_DESTROY :
    {
      PostQuitMessage(0);  
      return 0;            
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

HWND Init(const char* Caption,int Width, int Height)
{
  WNDCLASS wc;
  HWND  WinHwnd;

  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = MainProc;
  wc.cbClsExtra = wc.cbWndExtra = 0;
  wc.hInstance = _hInstance;
  wc.hIcon = LoadIcon(_hInstance, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
  wc.lpszMenuName = NULL;
  wc.lpszClassName = Caption;

  if (RegisterClass(&wc)) 
  {
    WinHwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
                             Caption, Caption,
                             WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                             CW_USEDEFAULT,CW_USEDEFAULT,
                             Width, Height,
                             HWND_DESKTOP, NULL, _hInstance, NULL);
  }
  else
  {
    WinHwnd = NULL;
  }
  return WinHwnd;
}

//------------------------------------------------------------
#pragma argsused
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,int nCmdShow)
{
  MSG msg;
  HWND hwnd = Init("Timer test",200,140);

  if (hwnd)
  {
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
      TranslateMessage(&msg); 
      DispatchMessage(&msg);  
    }
  }
  return msg.message;
}


To download the Timer class and the sample, click here. Vous pouvez télécharger la class Timer et l'exemple ici.