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.
 |
Bevor Sie an dieser Übung verzweifeln kann ich Ihnen auch wieder die Lösung
anzeigen. |
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!
 |
Wenn Sie genug graue Haare beim Lösen der Aufgabe bekommen haben kann ich Ihnen auch
meine Lösung anzeigen. |
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.
|