Timer unter Windows



  • Hallo an alle!

    Folgendes Problem: Ich möchte einen Timer im Programm setzen, der nach z.B. 5 sec eine bestimmte Aktion ausführt, das Programm soll bis dahin aber normal weiter laufen.

    Habe folgendes versucht:

    // Funktion, die beim Timeout ausgeführt werden soll
    void CALLBACK TimeOut(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    {
    	printf("Timeout!");
    }
    
    main()
    {
    UINT timerID; // ID für Timer;
    
    timerID = SetTimer(NULL,1,(UINT)5000,&(TIMERPROC)TimeOut); // Timer setzen auf 5 sec
    
    /* Hier folgt weiterer Code */
    }
    

    Alles läuft ohne Fehler, aber das Programm arbeitet einfach den weiteren Code ab, ohne dass die TimeOut Funktion jemals aufgerufen wird.
    Was mache ich falsch?



  • Hallo,

    Wireman schrieb:

    Was mache ich falsch?

    Nur die Dokumentation nicht richtig gelesen, denn:

    MSDN-Doku schrieb:

    An application can process WM_TIMER messages by including a WM_TIMER case statement in the window procedure or by specifying a TimerProc callback function when creating the timer. When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

    MfG,

    Probe-Nutzer



  • Danke für die Antwort, aber leider werd ich nicht so ganz schlau daraus.

    When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

    Was genau will mir dieser Text sagen? Was muss ich wo ändern? Wäre für ein paar erklärende Worte dankbar.



  • CALLBACKTimeOut
    fehlt da eventuell ein Leerzeichen 😃
    also CALLBACK TimeOut



  • Melan schrieb:

    CALLBACKTimeOut
    fehlt da eventuell ein Leerzeichen 😃
    also CALLBACK TimeOut

    Ne, war nur ein Tippfehler beim Schreiben des Beitrags.



  • du schreibst den code ab um ihn hier darzustellen?
    wegrofl



  • omfg schrieb:

    du schreibst den code ab um ihn hier darzustellen?
    wegrofl

    Na du scheinst ja ein besonders helles Köpfchen zu sein.
    Das Originalprogramm ist lange, verschachtelt und kompliziert. Da ist Copy/Paste
    nicht so einfach möglich, also habe ich das eigentliche Problem an einem kurzen Beispielcode verdeutlicht.
    Nachdem das hoffentlich zu deiner Zufriedenheit geklärt ist dürfte einer produktiven Antwort ja nichts mehr im Wege stehen.



  • Du kannst die Thematik über Waitable Timer realisieren.

    VOID CALLBACK TimerAPCProc(
       LPVOID lpArg,               // Data value
       DWORD dwTimerLowValue,      // Timer low value
       DWORD dwTimerHighValue )    // Timer high value
    
    {
    
       MessageBox(NULL, "Timer", "TEST", MB_OK);
    
    }
    
    int main( void ) 
    {
       HANDLE          hTimer;
       BOOL            bSuccess;
       __int64         qwDueTime;
       LARGE_INTEGER   liDueTime;
       MYDATA          MyData;
    
       hTimer = CreateWaitableTimer(
               NULL,                   // Default security attributes
               FALSE,                  // Create auto-reset timer
               TEXT("MyTimer"));  
    
       // Name of waitable timer
       if (hTimer != NULL)
       {
    
             // Create an integer that will be used to signal the timer 
             // 5 seconds from now.
             qwDueTime = -5 * _SECOND;
    
             // Copy the relative time into a LARGE_INTEGER.
             liDueTime.LowPart  = (DWORD) ( qwDueTime & 0xFFFFFFFF );
             liDueTime.HighPart = (LONG)  ( qwDueTime >> 32 );
    
             bSuccess = SetWaitableTimer(
                hTimer,           // Handle to the timer object
                &liDueTime,       // When timer will become signaled
                0,             // Periodic timer interval of 2 seconds
                TimerAPCProc,     // Completion routine
                NULL,          // Argument to the completion routine
                FALSE );          // Do not restore a suspended system
    
             if ( bSuccess ) 
             {
            	while(true)
                {
                   SleepEx(
                      0,            // Don't wait
                      TRUE );       // Put thread in an alertable state*/
                   	printf("Main\n");
                }
    
             } 
             else 
             {
                printf("SetWaitableTimer failed with error");
             }
    
       } 
    
       return 0;
    }
    

    Link zu Snycfunktionen.
    Link zu SleepEx.

    Gruß



  • Du könntest Dir auch einen kleinen Thread mit MessageSchleife schreiben:

    #include <windows.h>
    #include <process.h>
    #include <iostream>
    #include <conio.h>
    
    using namespace std;
    
    // Funktion, die beim Timeout ausgeführt werden soll
    void CALLBACK TimeOut(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    {
        printf("Timeout!\n");
    } 
    
    DWORD WINAPI MyThread (void * Param)
    {
       MSG msg;
    
       UINT timerID; // ID für Timer;
    
       timerID = SetTimer(NULL,1,(UINT)5000,&(TIMERPROC)TimeOut); // Timer setzen auf 5 sec
    
       while (GetMessage (&msg, NULL, 0, 0))
       {  
          DispatchMessage(&msg);
       }
    
       KillTimer(NULL, timerID);
    
       return 0;
    }
    
    void main(void)
    {
       int i;
       char c;
       DWORD dwThreadID;
       HANDLE hThread;
    
       hThread = CreateThread(NULL, 0, MyThread, NULL, 0, &dwThreadID);
    
       getch();
    
       PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
       WaitForSingleObject(hThread, INFINITE);
    
       CloseHandle(hThread);
    }
    


  • Hallo,

    Wireman schrieb:

    Danke für die Antwort, aber leider werd ich nicht so ganz schlau daraus.

    When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

    Was genau will mir dieser Text sagen? Was muss ich wo ändern? Wäre für ein paar erklärende Worte dankbar.

    Gerne. Da du nur eine einfache Konsolenanwendung schreiben willst, so wie es aussieht, gibt es einige Konsequenzen. Unter anderem müsstest du, um SetTimer zu verwenden, eine "Nachrichtenschleife" einbauen, was für Konsolenprogramme eher untypisch ist. Erst diese Schleife würde dafür sorgen, dass deine Timer-Funktion aufgerufen wird. Aber wenn das Programm weiter laufen soll, dann ist so ein Vorgehen sowieso nicht geeignet, ganz abgesehen davon, dass SetTimer für Konsolenprogramme überhaupt nicht verwendet werden sollte.

    Also müssen andere Lösungen her, es gibt einige, aber die einfachste wären wohl der Einsatz von Multimedia-Timer, die benötigen keine Nachrichtenschleife und warten in einem eigenen Thread, bis sie die bestimmte Aktion, die auch mit einer Callback-Funktion festgelegt wird, auslösen, siehe:

    http://msdn.microsoft.com/en-us/library/ms712704(VS.85).aspx

    MfG,

    Probe-Nutzer



  • Probe-Nutzer schrieb:

    Aber wenn das Programm weiter laufen soll, dann ist so ein Vorgehen sowieso nicht geeignet, ganz abgesehen davon, dass SetTimer für Konsolenprogramme überhaupt nicht verwendet werden sollte.

    Also müssen andere Lösungen her, es gibt einige, aber die einfachste wären wohl der Einsatz von Multimedia-Timer,

    Die Lösung mit dem Thread finde ich einfacher, und das Hauptprogramm läuft schön weiter. Was spricht dagegen?



  • Man kann es auch mehr oder weniger Plattformunabhngig gestalten, wenn man boost::thread benutzt:

    #include <boost/thread.hpp>
    
    //irgendwas, das ausgeführt werden soll
    void performSomeAction()
    {
        std::cout << "The action!\n";
    }
    
    //Funktion wird in Thread ausgelagtert und fuehrt jede Sekunde performSomeAction aus
    void timerFunction()
    {
        for(;;) //der Timer laeuft endlos
        {
            performSomeAction();
            boost::xtime xt;
            boost::xtime_get(&xt, boost::TIME_UTC);
            xt.sec += 1;
            boost::thread::sleep(xt);
        }
    }
    
    int main()
    {
        //Thread starten
        boost::thread t(timerFunction);
    
        //bla...
    
    }
    

    Man kann das noch schöner machen, aber das so erstmal als grobe Idee.



  • Belli schrieb:

    Probe-Nutzer schrieb:

    Aber wenn das Programm weiter laufen soll, dann ist so ein Vorgehen sowieso nicht geeignet, ganz abgesehen davon, dass SetTimer für Konsolenprogramme überhaupt nicht verwendet werden sollte.

    Also müssen andere Lösungen her, es gibt einige, aber die einfachste wären wohl der Einsatz von Multimedia-Timer,

    Die Lösung mit dem Thread finde ich einfacher, und das Hauptprogramm läuft schön weiter. Was spricht dagegen?

    Einfacher? Kommt darauf an, wie du "einfacher" definierst. Mit meinem Vorschlag kann Wireman sein bisheriges Programm fast übernehmen, er muss nur Funktionen tauschen, und nichts weiter beachten (ausser dem Hinzulinken einer weiteren Bibliothek und dem Hinzufügen des "richtigen" Headers), auch hier läuft das Programm weiter. Also deutlich weniger Code, um das gleiche zu erreichen, für mich ist das einfacher.

    Belli schrieb:

    Was spricht dagegen?

    Für mich das Vorhandensein einer Nachrichtenschleife, einer zusätzlichen Funktion, die eigentlich nur eine Nachricht, WM_TIMER, behandeln soll, nur damit eine Callback-Funktion aufgerufen werden kann. Dann besser noch

    MfG,

    Probe-Nutzer



  • Ja ... die Thread-Lösung schien MIR wohl deshalb einfacher, weil ich da alles nötige parat hatte; in die Thematik mit dem MulitMediaTimer musste ich mich erst mal einlesen ...



  • Danke an alle für die Antworten, ich werde die Varianten mal ausprobieren und sehen was am besten funktioniert.


Anmelden zum Antworten