winapi - SetTimer 与 CWnd::SetTimer

标签 winapi mfc

背景

MFC CWnd::SetTimer调用 WinAPI 的 SetTimer简单地。

_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); }

但是SetTimerCWnd::SetTimer文件不一样。

1. nIDEvent范围

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不依赖于回调参数,总是替换现有的计时器。但是CWnd::SetTimer取决于回调参数,如果指定了回调,可能会生成一个新的定时器 ID,因为 对于所有进程中的所有计时器,该值必须是唯一的 .

2. 返回值

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不生成新的定时器 id 并且不返回定时器 id,因此调用 KillTimernIDEvent范围。但是CWnd::SetTimer在某些情况下会生成新的计时器 ID,因此请调用 KillTimer与返回值。

因此,哪个文件是正确的?

我想使用 WinAPI SetTimer带有回调,它适用于我的电脑。但是如果某些平台不替换现有的计时器,我无法接受回调计时器。

最佳答案

据我所知,MFC 文档不正确。我已经进行了广泛的测试,只要窗口相同,计时器总是会替换以前的计时器。有回调和没有回调都是如此。

通过回调,我运行了以下测试:

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);

结果是(来自 VS 调试器跟踪):
timerID=2
timerID=2
timerID=4
timerID=4
nIDEvent=2, hWnd=0x00000000002d0bb8
nIDEvent=4, hWnd=0x00000000002d0bb8
nIDEvent=4, hWnd=0x0000000000140bd0

最后一次 SetTimer 调用使用了不同的窗口,它两次提供了相同的事件。但每次,返回值都与传递给计时器的值相同。并且 nIDEvent 使用相同的值。

CWnd 文档要么是过时的,要么是出于极其谨慎的考虑——我们知道返回值是 ID,所以我们应该始终使用它。

然而,一种说法显然是错误的:

对于回调定时器,所有进程中的所有定时器的值必须是唯一的

我刚刚演示了我可以在同一个进程中使用相同的 ID 两次,并且仍然接收两个事件。

关于winapi - SetTimer 与 CWnd::SetTimer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51010727/

相关文章:

Windows 资源管理器备用数据流外壳扩展

c++ - 看不到我的第一个窗口应用程序

c++ - 如何从控制台启动可执行文件并使用Qt读取输出

c++ - 如何禁用 CComboBox 并使其可选择用于剪贴板复制?

windows - 使用 VS2017 增强系统 DPI 缩放

c++ - 如何从 MFC 应用程序调用函数的 Win32 API 版本?

c++ - (C++/win32) 隐藏一个窗口,使用户不能 alt-tab 或切换到它

c# - 从文件中提取特定的图标层,然后将其保存为具有透明度的 .ico 文件

c++ - 如何初始化堆以便它可以被常规 MFC dll 中的静态构造函数使用?

MFC 应用程序在关闭时崩溃