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

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

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

Где TimerCallBack это:

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

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

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

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

Ответы на вопрос(4)

ип 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 внутри.

 Tebe21 нояб. 2013 г., 15:55
Не так чисто, если у вас есть консольное приложение. HWND там равен нулю .... Играть с idEvent нехорошо
 JWWalker29 мар. 2014 г., 00:41
Возможно, вы захотите отправить несколько сообщений с одними и теми же пользовательскими данными, но у вас не может быть нескольких таймеров с одним и тем же идентификатором. Поэтому я не думаю, что этот ответ так же универсален, как принятый ответ.
 Mickaël Pointier29 мая 2013 г., 10:34
Я сделал это сам, насколько я могу судить, он работает нормально. По моему мнению, это намного лучше, чем управлять статической картой указателей, возможно, намного безопаснее в многопоточном мире.
 paulm20 июн. 2013 г., 14:01
Это должен быть самый высокий рейтинг ответа, это самая чистая душа
 Robinson28 июн. 2015 г., 14:31
Проблема в том, что если ваш «бар» истек (был удален), вы все равно можете получить сообщение таймера, поэтому я бы не рекомендовал его в общем случае (или вообще). Карта идентификатора таймера к weak_ptr лучше.

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 в качестве идентификатора функции для вызова.

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

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
Решение Вопроса

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

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

Примерно так (так как это вопрос c ++, я просто реализую быстрый и грязный тип функтора, чтобы 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
 GJ.10 янв. 2011 г., 09:14
Мне очень понравилась ваша идея реализовать его с помощью шаблона, есть несколько реализаций в сети, которые делают это с помощью другого потока, но способ, которым вы реализовали это, очень хорош, хотя я не слишком хорош в шаблонах, я пытался скомпилируйте ваш код, но получите несколько ошибок, которые я не могу исправить. Я сделал следующие изменения в вашем коде. 1. класс ClassInstance расширяется от экземпляра 2. имя класса изменено с TimerManager на TimerMgr 3. timerID заменено на timerId в функции TimerProc, но сейчас я получаю следующие ошибки, которые не могу исправить.
 GJ.10 янв. 2011 г., 09:17
Ошибка 1: ошибка C2664: «SetTimer»: невозможно преобразовать параметр 4 из «void (__stdcall *) (HWND, UINT, UINT_PTR, int)» в «TIMERPROC» и ошибка компоновщика «private: статический класс std :: map <unsigned int, класс TimerMgr :: Instance *, структура std :: less <unsigned int>, класс std :: allocator <struct std :: pair <unsigned int const, класс TimerMgr :: Instance *>>> TimerMgr :: _ timers "(? _timers) @ TimerMgr @@ 0В? $ @ карта IPAVInstance @ TimerMgr @@ U? $ меньше @ I @ станд @@ V? $ распределитель @ U? $ пары @ $ @ CBIPAVInstance TimerMgr @@@ станд @@@ 4 @@ станд @@ A) Я думаю, что это имеет отношение к статическим данным, не могли бы вы помочь мне с этим.
 Moo-Juice07 янв. 2011 г., 14:19
+1, ты должен был пойти и сделать потрясающий ответ :)
 User06 апр. 2012 г., 03:30
Будет ли это работать, если я буду использоватьstd::shared_ptr<T> вместоT*?
 Chris Becke10 янв. 2011 г., 09:19
Извините, я написал слепой код со своего Mac, поэтому у меня не было возможности собрать его в компиляторе. Я обновлю это сейчас.

Ваш ответ на вопрос