SetTimer für die MessageQueue eines Threads



  • Hallo ich habe ein kleines Problem mit SetTimer. In meinem aktuellen Projekt benutze ich einen Thread mit einer MessageQueue. Ab und an kann es sein, dass sehr viele Messages eintreffen. Da die Abarbeitung relativ lange dauert und ich immer nur an der letzten Message interessiert bin, will ich beim Eintreffen einer Message einen Timer starten, der wenn er abgelaufen ist, die Abarbeitung der Message startet. Kommt aber während der Timer noch aktiv ist eine neue Message rein, so wird
    der Timer zurückgesetzt. Dies funktioniert normalerweise mit der Methode:

    UINT_PTR WINAPI SetTimer(
      __in_opt  HWND hWnd,
      __in      UINT_PTR nIDEvent,
      __in      UINT uElapse,
      __in_opt  TIMERPROC lpTimerFunc
    );
    

    Da ich aber leider in MessageQueue eines Threads arbeite, und der Timer eine Message an den Thread schicken soll und nicht an die MessageQueue des MainWindows, kann ich diese Methode nicht verwenden. Gibt es nicht auch einen Timer der nach Ablauf eine Message für die MessageQueue meines Threads erzeugt?

    Anbei noch kurz ein Ausschnitt des Codes meines Threads ums verständlicher zu machen:

    DWORD WINAPI MyParser:ParserControllerThreadProc(LPVOID lpParameter)
    MSG msg;
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    BOOL bRet = FALSE;
    while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
    { 
    
    	if(msg.message == WM_MYMESSAGE){
    	//Starte timer
    	SetTimer(xxx, TIMERID, 100, 0);
    
    	}else if(msg.message == WM_TIMER)
    	{
    
    			if (msg.wParam == TIMERID)   {
    
    			 //DO WORK HERE
    			}
    
              }
    
    }
    

  • Mod

    Verwende doch einen Timer mit Callback (hWnd für SetTimer ist dann NULL!



  • Wäre es nicht ausreichend so lange Messages abzuholen bis die Queue leer ist, und dann sofort mit der Abarbeitung der zuletzt empfangenen Message zu beginnen?

    Wenn du wie beschrieben einen Timer verwendest, besteht die Gefahr, dass du nie anfängst irgendeine Message zu bearbeiten. Nämlich dann, wenn permanent neue Messages eintreffen, bevor der Timer jemals abläuft.

    Wenn du dagegen Rechenzeit sparen willst, würde es sich anbieten einen Timer zu verwenden, diesen aber nicht zurückzusetzen wenn eine neue Message eintrifft.



  • Hallo, danke für die Antworten.

    SetTimer(NULL,TIMERID, 100, NULL);
    

    Hab ich schon probiert, allerdings kommen dann die Nachrichten nach Ablauf des Times nie an. Selbst wenn ich mir alle WM_TIMER Nachrichten über msg.wParam ausgeben lasse, so kommt nie eine mit der TIMERID an. Mache ich da in meinem Thread irgendetwas falsch?

    @hustbaer: Das mit einem Timer ist die optimale Lösung für mein Problem, das paßt schon so.


  • Mod

    Du solltest auch eine Callback Routine definieren.

    Ich weiß jetzt auch was Dein Denkfehler ist. WM_TIMER wird nie in einer Message auftauchen. Die wird direkt als Pseudonachricht an ein Fenster gesendet (Per SendMessage) oder die Callback Routine aufgerufen....



  • Danke für den Tip, aber irgendwie will das nicht funktionieren.
    Ich hab mir nun eine Callbackmethode gemacht. Diese habe ich im Header als static deklarieren müssen, weil sonst die Sigantur mit TimerProc nicht übereingestimmt hätte.

    VOID CALLBACK MyParser::MyTimerProc( 
        HWND hwnd,        // handle to window for timer messages 
        UINT message,     // WM_TIMER message 
        UINT idTimer,     // timer identifier 
        DWORD dwTime)     // current system time 
    { 
    
    	printf("TIMER EVENT IS HERE");
    }
    

    SetTimer rufe ich nun so auf:

    SetTimer(NULL,TIMERID, 100,MyTimerProc);
    

    Trotzdem wird die Callback Routine nie aufgerufen. Irgendwie ist da der Wurm drinnen. 😡


  • Mod

    Sie sollte aufgerufen werden wenn eine Message Loop läuft! Ohne Message Loop läuft funktioniert SetTimer nicht.



  • Aber in dem Thread habe ich doch einen MessageLoop oder verstehe ich da was falsch. Hier nochmal die Implementation von dem Thread:

    DWORD WINAPI MyParser:ParserControllerThreadProc(LPVOID lpParameter){
    MSG msg;
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    BOOL bRet = FALSE;
    while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
    { 
    
        if(msg.message == WM_MYMESSAGE){
        //Starte timer
        SetTimer(NULL,TIMERID, 100,MyTimerProc); 
        }else  {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }
    }
    

    Sorry für meine Begriffsstutzigkeit, ich bin halt noch ein relativer Anfänger in C/C++.


  • Mod

    Dein Code sieht OK aus.
    Wird SetTimer auch wirklich aufgerufen, ich benutze solche Timer Callbacks regelmässig, wenn ich kein Fensterhandle habe.

    Anosnten lies mal die Doku zu SetTimer. Die Timer ID sollte 0 sein, wenn SetTimer mit einem NULL hWnd verwendet wird. Die ID bestimmt das System und gibt dese zurück.



  • Ja wird aufgerufen, und ich habs nun auch mit ID = 0 versucht ohne Erfolg. Egal - ich werde mich nun nicht mehr weiter quälen, in der Endversion wird es eine MessageQueue mit einem HWND geben, und ich werde dann die Message des Timers dann dahin versenden. Die neue Funktionalität kann ich dann eben erst später testen. Danke nochmal für die Hilfe Martin. 🙂

    Beste Grüße
    Pezibär


Anmelden zum Antworten