Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SetTimer vs CWnd::SetTimer

Tags:

winapi

mfc

Background

MFC's CWnd::SetTimer calls WinAPI's SetTimer simply.

_AFXWIN_INLINE UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT nElapse,
        void (CALLBACK* lpfnTimer)(HWND, UINT, UINT_PTR, DWORD))
    { ASSERT(::IsWindow(m_hWnd)); return ::SetTimer(m_hWnd, nIDEvent, nElapse,
        lpfnTimer); }

But SetTimer and CWnd::SetTimer documents are not same.

1. nIDEvent parameter

SetTimer:
If the hWnd parameter is not NULL and the window specified by hWnd already has a timer with the value nIDEvent, then the existing timer is replaced by the new timer. When SetTimer replaces a timer, the timer is reset.

CWnd::SetTimer:
Specifies a nonzero timer identifier. If the timer identifier is unique, this same value is returned by SetTimer. Otherwise, SetTimer determines a new unique value and returns that. For a window timer (which has a NULL callback function), the value must be unique only for other windows timers that are associated with the current window. For a callback timer, the value must be unique for all timers in all processes. Therefore, when you create a callback timer, it is more likely that the returned value might differ from the value you specify.

SetTimer does not depend on callback parameter and always replaces the existing timer. But CWnd::SetTimer depends on callback parameter, and if callback specified, a new timer ID may be generated because the value must be unique for all timers in all processes.

2. Return value

SetTimer:
If the function succeeds and the hWnd parameter is not NULL, then the return value is a nonzero integer. An application can pass the value of the nIDEvent parameter to the KillTimer function to destroy the timer.

CWnd::SetTimer:
The timer identifier of the new timer if the function is successful. This value may or may not be equal to the value passed in through the nIDEvent parameter. An application should always pass the return value to the KillTimer member function to kill the timer.

SetTimer does not generate new timer id and does not return timer id, so call KillTimer with nIDEvent parameter. But CWnd::SetTimer generates new timer id in some case, so call KillTimer with the returned value.

Thus, which document is correct?


I want to use WinAPI SetTimer with a callback and it works on my pc. But if some platform does not replace the existing timer, I could not accept the callback timer.

like image 892
sayuri Avatar asked Nov 07 '22 05:11

sayuri


1 Answers

MFC documentation is incorrect, as far as I can tell. I have tested extensively, and the timers always replace the previous timer, provided the window is the same. This is true with callbacks and without.

With a callback, I ran the following test:

static void CALLBACK MyTimerProc(HWND hWnd, UINT nMsg, UINT_PTR nIDEvent, DWORD dwTime) {
    KillTimer(hWnd, nIDEvent);
}
...
timerID = SetTimer(2, 1000, MyTimerProc);
timerID = SetTimer(2, 1100, MyTimerProc);
timerID = SetTimer(4, 1200, MyTimerProc);
timerID = GetParentFrame()->SetTimer(4, 1300, MyTimerProc);

Results were (from VS debugger trace):

timerID=2
timerID=2
timerID=4
timerID=4
nIDEvent=2, hWnd=0x00000000002d0bb8
nIDEvent=4, hWnd=0x00000000002d0bb8
nIDEvent=4, hWnd=0x0000000000140bd0

The last SetTimer call used a different window, which gave the same event twice. But every time, the return value is identical to the value passed for the timer. And the same value is used for nIDEvent.

The CWnd documentation is either out of date, or acting out of extreme caution--we know the return value is the ID, so we should always use that.

However, one statement is clearly false:

For a callback timer, the value must be unique for all timers in all processes

I just demonstrated I could use the same ID twice, within the same process, and still receive both events.

like image 67
Dan Avatar answered Nov 28 '22 07:11

Dan