我在Class的函数中调用SetTimer。
SetTimer(NULL, 0, 10000, (TIMERPROC) TimerCallBack);
TimerCallBack的位置是:
static VOID CALLBACK TimerCallBack(HWND, UINT, UINT, DWORD)
现在我需要调用一个启动计时器的类方法,因为TimerCallBack是静态的,它不再能访问类对象。
我无法找到任何方法将对象指针与SetTimer一起传递,以便我可以在回调函数上接收它。
有没有其他方法可以实现这一点,如果不支持使用SetTimer,那么我可以实现这一点。
答案 0 :(得分:19)
您不需要地图。使用idEvent参数。微软给它UINT_PTR类型,所以它适合指针,即使是64位:
SetTimer(hwnd, (UINT_PTR)bar, 1000, MyTimerProc);
void CALLBACK MyTimerProc(HWND, UINT, UINT_PTR idEvent, DWORD)
{
Bar* bar = (Bar*)idEvent;
}
请注意,您需要使用实际的HWND。如果HWND为null,Windows将在内部生成idEvent。
答案 1 :(得分:9)
显然,如果你在一个窗口指导定时器消息,你可以只用窗口存储用户数据。
使用TimerProc执行此操作的唯一方法是创建一个类,用于管理用户数据对象的定时器ID的静态范围映射。
这样的事情(因为这是一个c ++问题,我只是实现了一个快速而肮脏的仿函数类型的东西,以便TimerMgr可以直接向类成员安排回调,这通常是你试图存储用户数据的原因: / p>
// Timer.h
#include <map>
class CTimer {
public:
class Callback {
public:
virtual void operator()(DWORD dwTime)=0;
};
template<class T>
class ClassCallback : public Callback {
T* _classPtr;
typedef void(T::*fnTimer)(DWORD dwTime);
fnTimer _timerProc;
public:
ClassCallback(T* classPtr,fnTimer timerProc):_classPtr(classPtr),_timerProc(timerProc){}
virtual void operator()(DWORD dwTime){
(_classPtr->*_timerProc)(dwTime);
}
};
static void AddTimer(Callback* timerObj, DWORD interval){
UINT_PTR id = SetTimer(NULL,0,interval,TimerProc);
// add the timer to the map using the id as the key
_timers[id] = timerObj;
}
static void CALLBACK TimerProc(HWND hwnd,UINT msg,UINT_PTR timerId,DWORD dwTime){
_timers[timerId]->operator()(dwTime);
}
private:
static std::map<UINT_PTR, Callback*> _timers;
};
// In Timer.cpp
#include <windows.h>
#include <Timer.h>
std::map<UINT_PTR,CTimer::Callback*> CTimer::_timers;
// In SomeOtherClass.cpp
class CSomeClass {
void OnTimer1(DWORD dwTime){
}
public:
void DoTimerStuff(){
CTimer::AddTimer( new CTimer::ClassCallback<CSomeClass>(this,&CSomeClass::OnTimer1), 100);
}
};
从地图中删除计时器留给读者练习:)
答案 2 :(得分:3)
鉴于您似乎没有使用MFC(CWnd::OnTimer
表示您可以访问该类),并且您没有HWND
(您可以想象设置一个属性在创建计时器的HWND上并将其返回到你的proc中,还有另一种选择。
SetTimer
返回UINT_PTR
,这是计时器ID。这是您在TimerProc
中获得的内容,并且在您完成后也将传递给KillTimer
。使用它,我们可以创建一个映射,将定时器ID映射到您创建的某个用户定义的对象:
class MyAppData
{
}; // eo class MyAppData
typedef std::map<UINT_PTR, MyAppData*> MyDataMap;
MyDataMap dataMap;
然后我们创建你的计时器:
MyAppData* _data = new MyAppData(); // application-specific data
dataMap[SetTimer(NULL, 0, 10000, (TIMERPROC)TimerCallBack)] = _data;
在您的程序中:
static void CALLBACK TimerCallBack(HWND _hWnd, UINT _msg. UINT_PTR _idTimer, DWORD _dwTime)
{
MyAppData* data = dataMap[_idTimer];
} // eo TimerCallBack
答案 3 :(得分:2)
还有一个解决方案,但为了使用该解决方案,SetTimer
中的non-NULL
参数应为SetWindowLongPtr
。
可以使用带参数GWLP_USERDATA
的API函数void SetLocalTimer(UINT_PTR nIDEvent, UINT nElapse)
{
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
SetTimer(nIDEvent, nElapse, _TimerRouter);
}
将额外数据存储到窗口中。
所以你可以在函数中加入你的类:
static void CALLBACK _TimerRouter(HWND hwnd, UINT, UINT_PTR nEventID, DWORD)
{
YourClassName* inst = (YourClassName*)GetWindowLong(hwnd, GWLP_USERDATA);
if( !inst )
return;
switch (nEventID)
{
case 0:
inst->DoThis();
break;
case 1:
inst->DoThat();
break;
}
}
和计时器路由器功能,它决定在给定的计时器上做什么。
{{1}}
这也允许使用nEventID作为函数标识符进行调用。
仍然存在从多个地方使用用户数据的风险,但我认为保持UI窗口匹配它的类模型这个指针是一种好的做法。