WM_TIMER Problem wegen einer Klasse



  • Hallo
    ich habe in einer Klasse eine Scroll Funktion fuer Text geschrieben:

    LCD::ScrollLineForward(char line,unsigned char* strInput,int iSpeed)
    {
    .
    .
    

    Nun habe ich das Problem das ich warten muss bis der Text gescrollt ist.
    Was ich aber nicht will, da ich den Text hin und her scrollen lassen will.

    Wär das alles nicht in einer Klasse hätte ich das mit WM_TIMER gelöst.
    Da ich die Klasse aber portabel machen will brauch ich etwas, das
    innerhalb der Zeit "iSpeed" wieder in die Funktion(in der Klasse) springt und einen Buchstaben weiter scrollt.
    Wie mache ich das?



  • Ovaron123 schrieb:

    Wär das alles nicht in einer Klasse hätte ich das mit WM_TIMER gelöst.
    Da ich die Klasse aber portabel machen will brauch ich etwas, das
    innerhalb der Zeit "iSpeed" wieder in die Funktion(in der Klasse) springt und einen Buchstaben weiter scrollt.
    Wie mache ich das?

    Portabel wirst du ohne 100% Prozessor schwer schaffen ...

    schon mal über ein SetTimer mit einem static Callback in der Klasse nachgedacht?

    M.T.



  • Naja mit Portabel meine ich, das ich die Klasse in mehreren Projekten von mir verwenden kann.

    Aber das mit dem "static callback" hört sich gut an, bloss wie funktioniert das? 😕



  • Achso 😉

    Du erstellst in der Klasse eine neue Funktion als static (Erklärung siehe C++ Handbücher) und übergibst diese als Callback bei SetTimer.

    M.T.



  • Hm hab mal ein bisschen probiert:

    class LCD
    {
    private:
           int nWritten;
           int DecrExOr(int key,char* szInput, char* szOutput);
    
           void CALLBACK TimerProc(HWND,UINT,UINT,DWORD);
           int iTimerId;
    .
    .
    

    und in der LCD.CPP

    void CALLBACK LCD::TimerProc(HWND,UINT,UINT,DWORD)
    {
    MessageBox(NULL,"Timer wurde aufgerufen!","LCD-CLASS",MB_ICONEXCLAMATION);
    }
    
    LCD::LCD(void)
    {
    	DecrExOr(XKEY,(char*)&CLASSVERSION,(char*)&LCDCLASSVERSION);  //HilfsVaribale mit entsclüssleter Version füllen.
    	hIOW = NULL; 
    	ixwidth = 0; iywidth = 0; ipipe = 0;
    	iTimerId = SetTimer(NULL,0,20000,this->TimerProc);
    }
    
    LCD::~LCD(void)
    {
    	KillTimer(NULL,iTimerId);
    	if (hIOW!=NULL) //wurde hIOW initalisiert
    	{ 
    		IowKitCloseDevice(hIOW);
    	}
        MessageBox(NULL,"Destruktor wurde Aufgerufen","LCD-CLASS",MB_ICONEXCLAMATION);   //Debug Zwecke
    }
    

    aber es kommt immer ein Fehler und zwar:

    Compiling...
    LCD.cpp
    f:\work\programming\visualnet\test1\LCD.cpp(29) : error C2664: 'SetTimer' : cannot convert parameter 4 from 'void (HWND,UINT,UINT,DWORD)' to 'TIMERPROC'
            None of the functions with this name in scope match the target type
    

    wie kann ich TimerProc in einer Klasse aufrufen?
    this->TimerProc geht wohl nicht 😞

    Und wiso Static? Wenn ich mehrere Objekte der Klasse habe hab ich doch bloss ein Timer.



  • Du musst dann eben selber über die Timer-ID entsprechend unterscheiden.
    Oder sowas: http://www.codeproject.com/cpp/thunk.asp?target=CThunk



  • Also ich weiß nicht, das mit dem CThunk ist mir etwas zu kompliziert.
    Das versteh ich noch nicht.
    Ich hab da mal was gefunden:
    http://www.codeproject.com/cpp/SetTimer__non-static.asp

    Der benutzt eine statisch und eine NICHT statische Callback Funktion.
    Mit der Statischen ruft er die NICHT statische auf.

    class CSleeperThread : public CWinThread {
    public:
      static VOID CALLBACK TimerProc_Wrapper( HWND hwnd, UINT uMsg, 
                                      UINT idEvent, DWORD dwTime );
      VOID CALLBACK TimerProc( HWND hwnd, 
                           UINT uMsg, UINT idEvent, DWORD dwTime );
      void ThreadMain();
      void WakeUp();
    private:
      static void * pObject;
      UINT_PTR pTimer;
      CRITICAL_SECTION lock;
    };
    

    Was ich aber hier auch nicht verstehe ist das mit den "CriticalSection":

    VOID CALLBACK CSleeperThread::TimerProc(HWND hwnd, 
         UINT uMsg, UINT idEvent, DWORD dwTime) {
     ::EnterCriticalSection(&lock);
     if(idEvent == pTimer) {
       KillTimer(NULL, pTimer);  // kill the timer so it won't fire again
       ResumeThread();  // resume the main thread function
     }
     ::LeaveCriticalSection(&lock);
    }
    

    Und wo füge ich hier den Code von meiner Funktion an? nach ::LeaveCriticalSection(&lock);?



  • Hi,

    Also ich weiß nicht, das mit dem CThunk ist mir etwas zu kompliziert.

    Ich habe dir trotzdem mal ein Beispiel zusammengestellt, das einen Thunk nutzt. Allerdings einen anderen als den von flenders genannten, da er nur unter Visual C++ funktioniert. Dieser sollte eigentlich mit jedem Compiler funktionieren und auf jeder x86er 32 Bit Windows-Platform laufen:

    #include <windows.h>
    #include <iostream>
    #include <assert.h>
    
    template<typename ObjectType, typename StaticFunctionType> class CallbackThunk 
    {
    public:
    	CallbackThunk(ObjectType* pObject, StaticFunctionType staticFunction)
    	{
    		*reinterpret_cast<ULONG*>(&instructions_[0]) = 0x042444C7;
    		*reinterpret_cast<ULONG*>(&instructions_[4]) = PtrToUlong(pObject);
    		*reinterpret_cast<BYTE*>(&instructions_[8]) = 0xE9;
    		*reinterpret_cast<ULONG*>(&instructions_[9]) = PtrToUlong(staticFunction) - (PtrToUlong(this) + sizeof(CallbackThunk));
    
    		::FlushInstructionCache(::GetCurrentProcess(), this, sizeof(CallbackThunk));
    	}
    
    	operator StaticFunctionType() const
    	{
    		return getCodeAddress();
    	}
    
    	StaticFunctionType getCodeAddress() const
    	{
    		return StaticFunctionType(this);
    	}
    private:
    	char instructions_[13];
    };
    
    template<typename Type> class Timer
    {
    public:
    	Timer(Type* pObject, void (Type::*pOnTimerElapsed)())
    		: identifier_(0)
    		, callbackThunk_(this, timerCallback)
    		, pObject_(pObject)
    		, pOnTimerElapsed_(pOnTimerElapsed)
    	{
    	}
    
    	~Timer()
    	{
    		if(identifier_ != 0)
    		{
    			kill();
    		}
    
    	}
    
    	void set(UINT interval)
    	{
    		assert(identifier_ == 0);
    
    		identifier_ = ::SetTimer(NULL, 0, interval, callbackThunk_);
    	}
    
    	void kill()
    	{
    		assert(identifier_ != 0);
    
    		::KillTimer(NULL, identifier_);
    
    		identifier_ = 0;
    	}
    private:
    	static void CALLBACK timerCallback(HWND windowHandle, UINT message, UINT_PTR identifier, DWORD time)
    	{
    		// Der Thunk hat den Parameter windowHandle, den wir sowieso nicht brauchen,
    		// durch den Zeiger auf die Timer-Instanz ersetzt.
    		Timer* pTimer = reinterpret_cast<Timer*>(windowHandle);
    		pTimer->call();
    	}
    	void call()
    	{
    		// Aufruf der im Konstruktor festgelegten Memberfunktion
    		(pObject_->*pOnTimerElapsed_)();
    	}
    private:
    	UINT_PTR identifier_;
    	CallbackThunk<Timer, TIMERPROC> callbackThunk_;
    	Type* pObject_;
    	void (Type::*pOnTimerElapsed_)();
    };
    
    class LCD
    {
    public:
    	LCD()
    		: timer_(this, &LCD::onTimerElapsed)
    	{
    		timer_.set(1000);
    	}
    
    	void onTimerElapsed()
    	{
    		std::cout << "onTimerElapsed" << std::endl;
    	}
    private:
    	Timer<LCD> timer_;
    };
    
    int main()
    {
    	LCD lcd;
    
    	MSG message;
    	while(GetMessage(&message, NULL, 0, 0))
    	{
    		DispatchMessage(&message);
    	}
    }
    

Anmelden zum Antworten