Idle Verarbeitung

In dieser Lektion befassen wir uns nochmals mit der Nachrichtenverarbeitung. Wie Sie in einer der vorherigen Lektionen erfahren haben, enthält die Klasse CWinThread bzw. die in der Anwendung davon abgeleitete Klasse CWinApp für die Nachrichtenbearbeitung die Methode Run(....). Run(...) holt die Nachrichten für die Fenster der Anwendung mittels der API-Funktion PeekMessage(...) aus der Nachrichtenschlange. Und PeekMessage(...) überprüft lediglich nur ob eine Nachricht vorliegt oder nicht. Liegt eine Nachricht vor, so wird diese im weiteren Verlauf von Run(...) verarbeitet. Doch was passiert in der MFC wenn keine Nachricht vorliegt? Diesem 'Geheimnis' werden wir in dieser Lektion einmal nachgehen.

Liefert der Aufruf von PeekMessage(...) den Wert 'FALSE' zurück (gleich keine Nachricht vorhanden), so wird von der MFC die CWinApp-Methode OnIdle(...) aufgerufen. Durch überschreiben dieser Methode können Sie in der Anwendung quasi eine Hintergrund-Aktivität ausführen. Die Methode erhält als Parameter einen Zähler der bei jedem Aufruf der Methode um eins erhöht wird. Trifft irgendwann eine Nachricht ein so wird dieser Zähler wieder zurückgesetzt. OnIdle(...) besitzt als Returnwert einen BOOL-Wert. Liefert die Methode den Wert TRUE zurück, so wird die Methode auch weiterhin aufgerufen wenn noch keine Nachricht vorliegt. Liefert sie dagegen den Wert 'FALSE', dann wird die OnIdle(...) Methode solange nicht mehr aufgerufen, bis eine neue Nachricht eintrifft. Ist diese neue Nachricht verarbeitet beginnt das ganze Spiel wieder von vorne, wobei der übergebene Zähler dann wieder mit dem Wert '0' startet.

Und noch ein wichtiger Hinweis:

Wenn Sie die Methode OnIdle(...) überschreiben, so müssen Sie immer die Basisklassen-Methode aufrufen. Dies Basisklassen-Methode führt im Hintergrund einige Aufräumarbeiten durch. Außerdem ist die Basisklassen-Methode z.B. auch dafür zuständig, dass die Tooltipps (das sind die kleinen gelben Hinweise) dargestellt werden.

Bauen wir uns nun ein kleines Beispiel das in der Titelzeile die aktuelle Uhrzeit anzeigen soll.

Erstellen Sie zunächst wieder mittels des Anwendungs-Assistenten ein SDI-Projekt ohne Doc/View Unterstützung. Geben Sie dem Projekt den Namen IdleMsg.

So, als nächstes gilt des die Methode OnIdle(...) zu überschreiben. OnIdle(...) ist, wie bereits ausführt, eine virtuelle Methode der Klasse CWinApp.

Versuchen Sie jetzt einmal OnIdle(...) mit Hilfe des Klassen-Assistenten zur Anwendung hinzuzufügen. In OnIdle(...) geben Sie vorerst nur einen kurzen Ton aus. Hierfür kann die bereits bekannte API-Funktion MessageBeep(...) verwendet werden. Damit OnIdle(...) nicht unnötige Rechenzeit verbraucht wird der Ton nicht bei jedem Aufruf ausgegeben. OnIdle(...) kann mehrere 1000-mal in einer Sekunde aufgerufen werden. Im Beispiel soll der Ton nur beim 1. und dann bei  jedem 65536. (gleich 0x10000) Aufruf ausgegeben werden.

Lösung zur OnIdle(...) Methode

Um die Idle-Verarbeitung zur Anwendung hinzuzufügen, klicken Sie in der Klassenansicht die Anwendungsklasse CIdleMsgApp mit der rechten Maustaste an. Wählen Sie aus dem Kontextmenü den Eintrag Virtuelle Funktion hinzufügen... aus. In dem daraufhin angezeigten Dialog führen Sie auf den Eintrag OnIdle einen Doppelklick aus und der Klassen-Assistent fügt die OnIdle(...) Methode zur Klasse hinzu. Anschließend wir innerhalb der Methode der erwähnte Zähler abgefragt und in Abhängigkeit davon ein Ton mittels MessageBeep(...) ausgegeben.

BOOL CIdleMsgApp::OnIdle(LONG lCount)
{
    // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
    CWinApp::OnIdle(lCount);
    // Nur jeden 65536. Aufruf auswerten
    if ((lCount&0xFFFF) == 0)
    {
        // Einen Ton ausgeben
        MessageBeep(-1);
    }
    return TRUE;
}

Ende der Lösung

Übersetzen und starten Sie anschließend das Beispiel. Sie sollten, solange Sie keine Aktionen mit dem Fenster durchführen, mehrere Töne in der  Sekunde hören.

Fahren Sie jetzt einmal mit der Maus über das Fenster. Sie werden, während Sie die Maus bewegen, fast ständig Töne hören. Was passiert hier? Nun, wie Sie sich bestimmt erinnern wird der Parameter der OnIdle(...) Methode immer dann wieder mit 0 initialisiert, wenn eine Nachricht für das Fenster eingetroffen ist. Da jede Mausbewegung eine Nachricht erzeugt ist dadurch der Zähler ständig 0 und es wird permanent ein Ton ausgegeben. Beachten Sie bitte, dass das die Mausnachricht zunächst an das Fenster gerichtet ist und nicht an das Anwendungsobjekt. Die MFC leitet jedoch viele der Nachrichten auch an die Anwendung weiter.

Da die Ausgabe eines Tons auf Dauer aber etwas eintönig ist, werden wir jetzt die aktuelle Uhrzeit in der Titelzeile ausgeben. Hier stellt sich zunächst das Problem: wie komme ich an die Uhrzeit? Wenn Sie sich durch die Online-Hilfe durchwühlen werden Sie früher oder später auf die MFC Klasse CTime stoßen. Mit Hilfe dieser Klasse kann die aktuelle Uhrzeit ausgelesen und in ein CString-Objekt konvertiert werden. Bleibt nur noch die Ausgabe der Uhrzeit in der Titelzeile. Da die Titelzeile eine allgemeine Fenstereigenschaft ist, wird sich sicher innerhalb der Klasse CWnd auch hierfür eine entsprechenden Methode finden.

Versuchen Sie zunächst selbst die aktuelle Uhrzeit auszulesen und in einen String zu konvertieren. Wie vorhin erwähnt hilft Ihnen dabei die MFC Klasse CTime. Anschließend müssen Sie 'nur' noch die CWnd Methode finden, die die Titelzeile des Fensters setzt. Nutzen Sie zur Lösung dieser Aufgabe ausgiebig die Online-Hilfe!

Lösung zur Ausgabe der aktuellen Uhrzeit

BOOL CIdleMsgApp::OnIdle(LONG lCount)
{
    // TODO: Speziellen Code hier einfügen und/oder Basisklasse aufrufen
    CWinApp::OnIdle(lCount);
    // Nur jeden 65536. Aufruf auswerten
    if ((lCount&0xFFFF) == 0)
    {

        // Aktuelle Systemzeit holen
        CTime     CSysTime = CTime::GetCurrentTime();
        // und in einen String konvertieren
        CString CTimeString = CSysTime.Format("%H.%M:%S");
        // Systemzeit in Titelzeile darstellen
        m_pMainWnd->SetWindowText(CTimeString);
        // Einen Ton ausgeben
        MessageBeep(-1);
    }
    return TRUE;
}

Zuerst wird ein CTime-Objekt definiert und diesem Objekt mittels der statischen CTime Methode GetCurrentTime(...) die aktuelle Uhrzeit zugewiesen. Um die so erhaltene Uhrzeit in ein CString-Objekt umzuwandeln wird die CTime Methode Format(...) aufgerufen. Welchen Inhalt, sprich welche Zeitangaben, dieser String dann letztendlich enthält wird über den Formatstring der Methode spezifiziert. Der so erhaltene String wird dann anschließend mittels der CWnd Methode SetWindowText(...) als Fenstertitel eingesetzt.

Ende der Lösung

Das fertige Beispiel zu dieser Lektion finden Sie im Programmverzeichnis zum Kurs unter 03EPMMFC\IdleMsg.

Damit beenden wir die Behandlung der Leerlauf-Verarbeitung. In der nächsten Lektion können Sie sich noch einige Tipps&Tricks zum Fensterhandling ansehen.



Copyright © 2004

Senden Sie Emails mit Fragen oder Kommentaren zu dieser Website an: mailto:info@cpp-tutor.de
 Wolfgang Schröder, Lerchenweg 23, D-72805 Lichtenstein. Tel: +49 7129 6470