使用SetTimer传递用户数据

时间:2011-01-07 11:50:02

标签: c++ winapi visual-c++

我在Class的函数中调用SetTimer。

SetTimer(NULL, 0, 10000, (TIMERPROC) TimerCallBack);  

TimerCallBack的位置是:

static VOID CALLBACK TimerCallBack(HWND, UINT, UINT, DWORD)

现在我需要调用一个启动计时器的类方法,因为TimerCallBack是静态的,它不再能访问类对象。

我无法找到任何方法将对象指针与SetTimer一起传递,以便我可以在回调函数上接收它。

有没有其他方法可以实现这一点,如果不支持使用SetTimer,那么我可以实现这一点。

4 个答案:

答案 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);
  }
};

从地图中删除计时器留给读者练习:)


  • ClassCallback需要继承Callback。
  • 需要定义静态变量
  • 代码现在可以在MSVC 9.0上正确构建和运行

答案 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窗口匹配它的类模型这个指针是一种好的做法。