Timer mit APC-Aufruf



  • Hi,

    ich hab hier mal ein kleines Testprogramm geschrieben. Allerdings verstehe ich nicht, warum er nicht in TimerApc(...) springt, obwohl der Timer signalisiert wird.

    #include <iostream>
    #include <iomanip>
    
    using namespace std;
    
    #include "Windows.h"
    #include "Time.h"
    
    #define NAME_TI "Name_Ti_Test"
    HANDLE hTi_Test = NULL;
    
    void PrintTime (char *pcLabel) //die Ausgabe von Zeitstempeln
    {
      if (pcLabel == NULL)
        return;
    
      SYSTEMTIME sTime;
      GetSystemTime (&sTime);
    
      cout << endl << endl << pcLabel << ": " 
        << setw (2) << setfill('0') 
        << (sTime.wHour+1) << ":"
        << sTime.wMinute << ":"
        << sTime.wSecond << ","
        << setw (3) << setfill('0') 
        << sTime.wMilliseconds;
      return;
    }
    
    //-----------------------------------------------------------------------------
    
    void APIENTRY TimerApc (LPVOID, DWORD, DWORD) //<- hier will er nicht reinspringen
    {
      PrintTime ("ENDE");
      return;
    }
    
    //-----------------------------------------------------------------------------
    
    void ResetTimer(LARGE_INTEGER& rliTime) //Timer mit dem neuen Wert starten
    {
      if (hTi_Test != NULL)    
      {
        SetWaitableTimer (hTi_Test, &rliTime, 0, 
            (PTIMERAPCROUTINE)TimerApc, NULL, false);
        PrintTime ("START");
      }
    
      return;
    }
    
    //-----------------------------------------------------------------------------
    
    int main (int argc, char **argv)
    {
      hTi_Test = CreateWaitableTimer (NULL, false, NAME_TI);
      if (hTi_Test == NULL)
      {
        cout << endl << "Erstellung des Timers fehlgeschlagen!";
        return -1;
      }
    
      SYSTEMTIME sTime;
      LARGE_INTEGER liTime;
      liTime.QuadPart = 1;
    
      while (liTime.QuadPart != 0)
      {
        memset (&liTime, 0, sizeof(liTime));
    
        cout << endl << "Timerlaenge angeben [in 100ns]: ";
        cin >> liTime.QuadPart; //negative Werte eingeben
    
        if (liTime.QuadPart == 0)
          break;
    
        ResetTimer (liTime);
        if ((WaitForSingleObject (hTi_Test, 3000)) != WAIT_TIMEOUT)
          cout << endl << "Timer signalisiert!" << endl;
      }
    
      CancelWaitableTimer (hTi_Test);
      CloseHandle (hTi_Test);
      return 0;
    }
    

    Sicher ich könnte auch einen Thread auf das Ablaufen des Timers warten lassen, aber die Lösung für mein Problem über den Apc-Aufruf, wäre wesentlich eleganter.
    Als Compiler benutze ich den, der von Borland bei der BCB6-Umgebung mitgeliefert wird. Ich weiss allerdings nicht, in wie weit das wichtig ist.

    Hat jemand von euch damit Erfahrung?

    Maffe



  • WaitForSingleObjectEx ist die benötigte Funktion. WaitForSingleObject (ohne ex) wartet nicht auf APC.



  • Ich würde mal vemuten, weil Du versuchst, eine Windows-API in einem Consolenprogramm zu verwenden. APIENTRY ist ein Format, das für Windows-Programme genutzt wird, Deine "int main" ist aber nur ein DOS-Format.
    TimerApc erwartet drei Parameter (LPVOID, DWORD, DWORD), die Du beim aufruf nicht übergibst.



  • @mgs_:
    Ich hab das Ganze schon auf WaitForSingleObjectEx geändert. Funktioniert trotzdem nicht.

    @Electronix:
    Ich brauch ja auch nicht unbedingt, was übergeben, bei einem solch "einfachen" Aufruf. Das mit dem DOS ist ein Argument. Müsste dann nicht aber der Compiler sich beschweren? 😕



  • Ich hab's jetzt so geändert. Und so funktionierts.

    int main (int argc, char **argv)
    {
      hTi_Test = CreateWaitableTimer (NULL, false, NAME_TI);
      if (hTi_Test == NULL)
      {
        cout << endl << "Erstellung des Timers fehlgeschlagen!";
        return -1;
      }
    
      SYSTEMTIME sTime;
      LARGE_INTEGER liTime;
      liTime.QuadPart = 1;
    
      while (liTime.QuadPart != 0)
      {
        memset (&liTime, 0, sizeof(liTime));
    
        cout << endl << "Timerlaenge angeben [in 100ns]: ";
        cin >> liTime.QuadPart;
    
        if (liTime.QuadPart == 0)
          break;
    
        ResetTimer (liTime);
    
        if ((SleepEx (3000, true)) == 0)
          cout << endl << endl << "Timer noch nicht abgelaufen!" << endl;
        else
          cout << endl << endl << "APC-Aufruf!" << endl;
      }
    
      CancelWaitableTimer (hTi_Test);
      CloseHandle (hTi_Test);
      return 0;
    }
    

    Der Grund war:

    Jeffrey Richter - Microsoft Windows, Programmierung für Experten schrieb:

    HANDLE hTimer = CreateWaitableTimer (....);
    SetWaitableTimer (hTimer, ..., TimerAPCRoutine, ...);
    WaitForSingleObjectEx (hTimer, INFINITE, true);

    Derartiger Code ist zu vermeiden, da der Aufruf von WaitForSingleObjectEx in Wirklichkeit ein zweifaches Warten impliziert, einmal per Alarmierung und zum zweiten über ein Handle für ein Kernel-Objekt. Wenn der Zeitgeber in den signalisierten Zustand wechselt, war das Warten erfolgreich und der Thread wird aktiviert. Letzteres bewirkt ein Verlassen des alarmierbaren Zustands, weshalb die APC-Prozedur nicht aufgerufen wird. [...]


Anmelden zum Antworten