How to use SetTimer API

38,843

Solution 1

There are few problems with your program:

  1. C programs do end when they leave main() so there is no time when the timer can occur.
  2. Win32 timers need the message pump (see below) to be working, as they are implemented through WM_TIMER message, even when they are not associated with any window, and if you provide function callback.

    When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

    Source: MSDN: SetTimer function

  3. The callback function has bad prototype. See http://msdn.microsoft.com/en-us/library/windows/desktop/ms644907%28v=vs.85%29.aspx

    void CALLBACK f(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
    {
      printf("Hello");
    }
    
    int main() 
    {
      MSG msg;
    
      SetTimer(NULL, 0, 1000*60,(TIMERPROC) &f);
      while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    
      return 0;
    }
    

(Note this example program never ends, instead, real program should have some additional logic to do so by sending WM_QUIT).

Solution 2

I found the best implementation to be as follow:

#define TIMER1 1001
#define TIMER2 1002


    SetTimer(hWndMainWnd,             // handle to main window 
        TIMER1,            // timer identifier 
        1000, 
        NULL);     // no timer callback 

    SetTimer(hWndMainWnd,             // handle to main window 
        TIMER2,            // timer identifier 
        5000, 
        NULL);     // no timer callback 

Then, as part of the main event loop:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_TIMER:
        if (wParam == TIMER1)
        {
              // do something1
        }
        else
        if (wParam == TIMER2)
        {
             // do smoething2
        }
        break;
Share:
38,843
user2219913
Author by

user2219913

Updated on November 20, 2020

Comments

  • user2219913
    user2219913 over 3 years

    I tried to use SetTimer API to call a function every X minutes. So, i have written this test code

    void f()
    {
     printf("Hello");
    }
    int main() 
    {
     SetTimer(NULL, 0, 1000*60,(TIMERPROC) &f); 
    }
    

    I should have Hello written every minute but it does not work.

  • mity
    mity about 11 years
    @ComFreek: Thx for editing, can you tell me how you do that? When I mark some (bad) part as code, I then often have a problem how to fix it, the editor just behaves strange.
  • ComFreek
    ComFreek about 11 years
    Np ;) I replaced your <code> and <pre> tags by four indents (plus the indents of the list). See also here for editing help. I've also added a quote from MSDN stating your point #2.
  • user2219913
    user2219913 about 11 years
    thanks Mity, but if I have two methods and I want to call the first one every 15 minute and the other only one time in my programm
  • mity
    mity about 11 years
    You can use two timers, one of them stopped with KillTimer in its handler. Or use one timer with a flag variable and one if in the timer proc.
  • user2219913
    user2219913 about 11 years
    and if I have some code after the while that I want to execute.
  • mity
    mity about 11 years
    That's already unrelated question. Take into account that writing Win32 programs (and using the timers lead to that) implies event-driven programming. That's simply quite different approach from writing classical C program who takes input, does some computations and writes down some output.
  • IInspectable
    IInspectable over 6 years
    The OP doesn't have a window, and wants to use a callback instead. This answer doesn't address that scenario. Besides, the timer IDs need to be unique. Using hardcoded values will clash with other timer code. Use the address of an object with sufficient lifetime instead. That's why the ID is pointer-sized. Also, when copying code, proper attribution should come with it.
  • Michael Haephrati
    Michael Haephrati over 6 years
    The "window" parameter is optional and can be replaced by a NULL. See msdn.microsoft.com/en-us/library/windows/desktop/… to learn more. The answer addresses the exact scenario, and was fully tested. This is our own code and it works. Using hard-coded values won't clash with other timer code because all timers which are defined by SetTimer() are defined within the scope of the application. For the purpose of the answer, using these preprocessor definitions is the best thing to do.
  • IInspectable
    IInspectable over 6 years
    The window parameter is optional only, if you provide a callback. And when you do, the WM_TIMER message is no longer posted to your window. The window procedure is meaningless in that scenario. If you use a timer callback, the timer ID is no longer associated with a window, and must be globally unique across your application. An application that uses 3rd-party code cannot use hardcoded ID values without risking a collision. Using the address of an object with suitable lifetime is the conventional technique to ensure uniqueness.
  • Michael Haephrati
    Michael Haephrati over 6 years
    My answer has no flaws and was fully tested. You are either repeating things I already wrote or picking up irrelevant details (like the pre-processor directive I am using). I have really nothing more to add to what I wrote. There is nothing wrong using hardcoded ID in the scope of this answer, and even if 3rd party libraries are used, it should usually not interfere (and you can find that most 3rd libraries contain a mass of their own "DEFINE ..." and that CAN be a problem but it rare. When that happens you just change your definitions. This is NOT the reason not to use DEFINE.
  • IInspectable
    IInspectable over 6 years
    Timers can be used in two distinct ways: By handling the WM_TIMER message and by having the system invoke a callback. The OP is asking about the latter, while your answer explains how to use the former. Both ways have significant differences: Handling the timer (message handling in the window procedure vs. providing a callback) and scope of ID uniqueness (window vs. application). While this answer fails to explain, how to use timers with a callback, it also falls short of explaining, why the OP's code doesn't work. This answer is not useful.
  • Charlie Scott-Skinner
    Charlie Scott-Skinner about 5 years
    The C++ example from MSDN in the link you gave (and that I was already using) doesn't compile - thank you for showing the proper callback prototype