Подтвердить что ты не робот

Передача данных пользователя с помощью SetTimer

Я вызываю SetTimer в функции класса.

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

Где TimerCallBack:

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

Теперь мне нужно вызвать один из методов класса, который инициировал таймер, поскольку TimerCallBack статичен, он больше не имеет доступа к объекту класса.

Я не могу найти способ передать указатель объекта вместе с SetTimer, чтобы я мог вернуть его обратно на функцию обратного вызова.

Есть ли другой способ достичь этого, если он не поддерживается с помощью SetTimer, и каким другим способом я могу это реализовать.

4b9b3361

Ответ 1

Очевидно, что если вы направляете сообщения таймера в окне, вы можете просто сохранить пользовательские данные в окне.

Единственный способ сделать это с помощью TimerProc - создать класс, который управляет статично-ориентированной картой идентификатора таймера для пользовательских объектов данных.

Что-то вроде этого (так как это вопрос на С++, я просто реализую быструю и грязную работу типа функтора, чтобы TimerMgr мог организовывать обратные вызовы непосредственно членам классов, что обычно является причиной того, что вы пытаетесь сохранить пользовательские данные:

// 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

Вам не нужна карта. Используйте параметр idEvent. Microsoft дала ему тип 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 внутри.

Ответ 3

Учитывая, что вы не используете MFC (CWnd::OnTimer означает, что у вас будет доступ к классу), и у вас нет HWND (вы могли бы установить свойство HWND на создание таймера и вернуть его в свой proc), есть еще один вариант.

SetTimer возвращает UINT_PTR, который является идентификатором таймера. Это то, что вы получите в своем TimerProc, а также перейдете к KillTimer, когда закончите с ним. Используя это, мы можем создать карту, которая отображает идентификатор таймера на определенный пользователем объект вашего творения:

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

Ответ 4

Существует еще одно решение, но для использования этого параметра - HWND в SetTimer должно быть non-NULL.

Можно хранить дополнительные данные в самом окне, используя функцию API SetWindowLongPtr с параметром GWLP_USERDATA.

Итак, вы можете добавить в свой класс следующую функцию:

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

Это позволяет также использовать nEventID в качестве имени функции, который будет вызываться.

По-прежнему существует риск того, что данные пользователя будут использоваться из более чем одного места, но я считаю хорошей практикой держать окно UI, соответствующее ему, моделирует этот указатель.