Передача пользовательских данных с помощью SetTimer - PullRequest
13 голосов
/ 07 января 2011

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

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

Где TimerCallBack:

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

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

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

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

Ответы [ 4 ]

19 голосов
/ 24 ноября 2012

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

9 голосов
/ 07 января 2011

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

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

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

// 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
3 голосов
/ 10 февраля 2018

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

Можно сохранить дополнительные данные в самом окне, используя функцию APISetWindowLongPtr с параметром 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 в качестве идентификатора вызываемой функции.

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

3 голосов
/ 07 января 2011

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...