PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer mit VC6


MaikRutsche
2004-03-16, 13:26:15
Moin,

ich habe hier ne einfach Klasse - keine Vererbung von irgendwas:

class CmyClass
{
...
}

Wie kann ich einer einfachen Klasse einen Timer hinzufügen?

Bei Delphi geht das ja sehr simpel mit

m_Timer := TTimer.Create(nil);
...
m_Timer.OnTimer := OnTimer; //Funktion zuweisen die bei dem Timerereignis ausgeführt werden soll

So wie erledige ich nun das ganze mit VC 6?

Es gibt ja nun:
SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc );
... da wird ja nen Fensterhandle verlangt aber das habe ich ja nunmal nicht


Hat jemand ne Idee?

MfG

Maik

MaikRutsche
2004-03-16, 13:45:44
Ich habe jetzt das hier:


class CmyClass
{
private:
static void CALLBACK EXPORT OnTimer(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime);
...
}


Und erstelle einen Timer mit:

SetTimer (NULL, m_TimerID, m_ReadInterval, OnTimer);


Aber ich kann nun nicht innerhalb der Methode OnTimer() (wegen dem static) auf Membervariablen und -methoden zugreifen.

Doch ohne dem static kann ich die Methode bei SetTimer nicht übergeben :(

...

ethrandil
2004-03-16, 14:25:48
Also, ich komme von der Java-Sektion, und habe lange nix mit C++ gemacht, aber gehts nicht in die Richtung:

class CmyClass
{
private:
void doTimer();

static CmyClass myObject;
static void CALLBACK EXPORT OnTimer(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime){
if(myObject == NULL)
myObject = new CmyClass();
myObject.doTimer();
}
...
}
?

- Eth

(Wenn du mehrere CmyClass-Objekte hast, dann musst du halt einen statischen Vector nehmen, o.ä.)

Gnafoo
2004-03-16, 14:37:08
@ethrandil
Damit erstellt er aber eine neue Instanz der Klasse, was er
ja eigentlich nicht will. Außerdem ist myObject nicht unbedingt
NULL am Anfang.

Oft kann man bei Callbacks einen Pointer auf benutzerspezifische
Daten mitgeben (z.b. nen Pointer auf die Klasse selber). Da das
hier nicht der Fall ist, musst du wohl mit static arbeiten:


#include <cassert>
using namespace std;
// #include ...

class Bla
{
public:
Bla(void)
{
assert(mpThis==0);
mpThis = this;

SetTimer(0, mTimerID, mReadInterval, OnTimer);
}

virtual ~Bla(void) {}

protected:
static void CALLBACK EXPORT OnTimer(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
// mpThis->...
}

static Bla* mpThis;
};
Bla* Bla::mpThis = 0;


Allerdings ist dabei die Klasse ein Singleton. D.h. du kannst nur
eine Instanz davon haben. (mpThis wird von allen Klasseninstanzen
geshared)

mpThis erhält nen Pointer auf die Klasse bei der Initialisierung.
Wird eine zweite Instanz erstellt (ein zweiter Pointer nötig),
bricht das Programm wegen dem assert ab.

was besseres fällt mir grad nicht ein. :)

cu DerTod

Xmas
2004-03-16, 19:29:56
Eine relativ umständliche, aber machbare Lösung:

Du schreibst ein Modul mit zwei Funktionen, einmal der "TimerCallback", der aufgrund der Event-ID die entsprechenden Funktionen aufruft, und eine Funktion "RegisterTimer", die einen Zeiger auf die Instanz, einen Zeiger auf eine statische Methode oder Funktion, sowie alle weiteren Timer-Relevanten Parameter annimmt (die ID kann aber automatisch vergeben werden). RegisterTimer fügt nun diese Informationen in eine Tabelle ein und Startet einen neuen Timer, mit "TimerCallback" als Callback.

Wenn nun ein Timer-Ereignis auftritt, schaut TimerCallback auf die Event-ID, sucht in der Tabelle (die modul-global, also static sein kann) nach dem entsprechenden Eintrag, und ruft die registrierte statische Methode oder Funktion mit dem Zeiger auf die Instanz als Parameter auf.
Diese muss nun so aufgebaut sein, dass sie den Zeiger zum richtigen Objekttyp castet und dann damit die OnTimer-Methode aufruft.

Etwas Code:

// Modul meinTimer.cpp

static ? tabelle; // ? kann hier ein Listentyp sein

void CALLBACK EXPORT TimerCallback(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
// suche nIDEvent in tabelle
// in tabelle stehen Instanzzeiger und Funktionszeiger
// pseudocode:
(tabelle[nIDEvent].Funktionszeiger)(tabelle[nIDEvent].Instanzzeiger);
}

void RegisterTimer(void * instzeiger, void (*fktzeiger)(void*), int mReadInterval)
{
// generiere nIDEvent
//pseudocode:
tabelle[nIDEvent].Funktionszeiger = fktzeiger;
tabelle[nIDEvent].Instanzzeiger = instzeiger;
// Timer setzen
SetTimer(0, nIDEvent, mReadInterval, TimerCallback);
}
// Ende Modul meinTimer.cpp

// Klasse die den Timer verwendet:
class myClass
{
public:
static void tc(void * instanz) { (*((myClass*)instanz)).OnTimer(); }

void OnTimer() {}

myClass() { RegisterTimer((void*)this, tc, 10); }
};

Der Code dient nur zur Erläuterung und ist so nicht korrekt.

Maik Rutsche
2004-03-17, 12:50:14
Gut Danke erstmal.

Eine wirklich einfach Lösung scheints irgendwie nicht zu geben...

Aber es läuft erstmal.

Ich habe die Timerroutinen aus mmsystem.h genommen.

Da kann ich beim erstellen des Timers einen Zeiger übergeben, der beinhaltet bei mir die klasse, zu der der Timer gehören soll und ruft dann eine vorgegebene Methode auf...

Aber auf jeden Fall muss da ne bessere variante an start.

Gnafoo
2004-03-17, 16:07:23
Original geschrieben von Maik Rutsche
Gut Danke erstmal.

Eine wirklich einfach Lösung scheints irgendwie nicht zu geben...

Aber es läuft erstmal.

Ich habe die Timerroutinen aus mmsystem.h genommen.

Da kann ich beim erstellen des Timers einen Zeiger übergeben, der beinhaltet bei mir die klasse, zu der der Timer gehören soll und ruft dann eine vorgegebene Methode auf...

Aber auf jeden Fall muss da ne bessere variante an start.

Wenn du ne Möglichkeit hast der Timerfunktion nen Pointer
mitzugeben, lässt sich das ganze doch recht elegant lösen.
Ich mach mal ein Beispiel:


class TimerClass
{
public:
TimerClass(unsigned int interval)
{
// createTimer ist erfunden :) kenn die Funktionen
// aus mmsystem.h nicht
mTimerID = createTimer(&_timerFunc, interval);
}

virtual
~TimerClass(void)
{
// siehe createTimer :) auch erfunden
deleteTimer(mTimerID);
}

protected:
virtual void timerFunc(void) = 0;

private:
static _timerFunc(void* pThis)
{
// überschriebene timerFunc aufrufen
((TimerClass*)pThis)->timerFunc();
}

unsigned int mTimerID;
};


Dann kann jede beliebige Klasse von TimerClass erben und
die timerFunc-Funktion überschreiben, die dann per Timer
aufgerufen wird:


class StupidClass : public TimerClass
{
public:
StupidClass(void)
: TimerClass(1000) {}

virtual ~StupidClass(void) {}

protected:
void timerFunc(void)
{
cout << "Es ist eine Sekunde vergangen!" << endl;
}
};