[WinAPI] SetTimer 中定时器 ID 的另类使用

陪她去流浪 桃子 2016年08月26日 阅读次数:2754

以前在用 SetTimer 设置定时器的时候,新定时器的 ID 从来都是从 0 开始递增,通过宏定义的方式。这个 ID 是我们手动设置进去的,Windows 窗口内部并不关心这个值是多少,有何意义。

然而,今天在查看 SetTimer 的文档时,有注意到,它的原型其实是这样的:

UINT_PTR WINAPI SetTimer(
  HWND      hWnd,
  UINT_PTR  nIDEvent,
  UINT      uElapse,
  TIMERPROC lpTimerFunc
);

,它的 nIDEvent 被定义成的是 UINT_PTR,看到这个类型时,我若有所思:它应该被设置成任意一个值(当然包括指针在内)。当它是一个指针时,我们就可以将任何的上下文数据封装进结构体来传递了。于是我写了一个例子来简单验证:

#include <string>

#include <atlbase.h>
#include <atlwin.h>

class Window : public CWindowImpl<Window>
{
private:
	struct TimerContext {
		std::wstring msg;
	};

protected:
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		TimerContext* pCtx = new TimerContext;
		pCtx->msg = L"Timer context message";

		// 注意到,我直接把指针设置成定时器的ID
		SetTimer(UINT_PTR(pCtx), 5000);

		bHandled = FALSE;
		return 0;
	}

	LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		// 实际上,我们的定时器ID是一个指针值
		TimerContext* pCtx = reinterpret_cast<TimerContext*>(wParam);

		// 清除定时器
		KillTimer(UINT_PTR(pCtx));

		MessageBox(pCtx->msg.c_str());

		delete pCtx;

		return 0;
	}

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
		::PostQuitMessage(0);
		return 0;
	}

private:
	BEGIN_MSG_MAP(Window)
		MESSAGE_HANDLER(WM_TIMER, OnTimer)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
	END_MSG_MAP()
};

int __stdcall wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR lpCmdLine, int nShowCmd) {
	Window wnd;
	wnd.Create(nullptr, nullptr, nullptr, WS_OVERLAPPEDWINDOW);
	wnd.ShowWindow(SW_SHOWNORMAL);

	MSG msg;
	while (GetMessage(&msg, nullptr, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

,这是我头一回写 MFC/ATL/WTL 的完整程序。下面是效果图:

通过代码可以看到,我把一个指针设置成了新创建定时器的 ID。在收到定时器消息之后,又转制转换回结构体指针,取得其它数据(此处的 msg string),从而达到传递额外数据的功用。

但是我估计,在 OnTimer 窗口消息回调中这样使用意义不大,因为毕竟是成员函数。然而,如果指定了 SetTimer 的最后一个参数:回调函数 时,估计意义就大了。

再然而,实际上,我几乎没有见到有人设置了 SetTimer 的回调函数。所以,是否好用,还有待挖掘、考证。

源代码:Source.cpp

这篇文章的内容已被作者标记为“过时”/“需要更新”/“不具参考意义”。

标签:WinAPI