Ausgeschlafen - Reagieren auf Windows Events beim Systemstart nach Standby/Hibernate
Gerade bastle ich an einer kleinen Anwendung, mit der ich mir im System Tray immer die aktuell günstigsten Call-by-Call-Nummern anzeigen lassen kann. Wenn ich dann einen Anruf machen möchte, muss ich nur die Maus über das Tray Icon bewegen und sehe schon, welche Vorwahl ich wählen muss. Das finde ich einfacher, als eine Seite wie www.telefontarife.de im Browser aufzurufen oder gar ein Tarifvergleichsprogramm zu installieren.
Mein kleines Programm soll einfach im Hintergrund laufen, ein Tray Icon zeigen und periodisch seine Daten durch Zugriff auf ein Informationsangebot im Internet aktualisieren. Das ist auch alles kein Problem - solange ich meinen Laptop nicht schlafen lege. Meist fahre ich den Rechner am Tagesende (oder auch zwischendurch) nicht herunter, sondern schalte ihn nur in den Standby- oder Hibernate-Modus.
Wenn ich ihn aus einem dieser Modi wieder erwecke, läuft meine Anwendung zwar noch, aber das Timer-Control feuert nicht mehr periodisch. Das Programm aktualisiert seine Daten also nicht mehr automatisch.
Da andere Programme aber sehr wohl auf die Erweckung aus einem der Schlummerzustände reagieren (Outlook holt dann z.B. die Email ab), dachte ich mir, dass auch meine Anwendung sich über diese Systemzustandsänderung informieren lassen könnte. Wie könnte sie aber an die Ereignisse, die Windows zu diesem Zweck sicherlich wirft, herankommen? Die Antwort liegt im Windows Platform SDK unter dem Stichwort "Power Management".
Windows meldet Ereignisse des Power Management Systems mit der Windows Nachricht WM_POWERBROADCAST an alle Anwendungen:
#define WM_POWERBROADCAST 0x0218
Der wparam der Nachricht enthält dann einen der folgenden Werte:
#define PBT_APMQUERYSUSPEND 0x0000
#define PBT_APMQUERYSTANDBY 0x0001
#define PBT_APMQUERYSUSPENDFAILED 0x0002
#define PBT_APMQUERYSTANDBYFAILED 0x0003
#define PBT_APMSUSPEND 0x0004
#define PBT_APMSTANDBY 0x0005
#define PBT_APMRESUMECRITICAL 0x0006
#define PBT_APMRESUMESUSPEND 0x0007
#define PBT_APMRESUMESTANDBY 0x0008
#define PBTF_APMRESUMEFROMFAILURE 0x00000001
#define PBT_APMBATTERYLOW 0x0009
#define PBT_APMPOWERSTATUSCHANGE 0x000A
#define PBT_APMOEMEVENT 0x000B
#define PBT_APMRESUMEAUTOMATIC 0x0012
Davon sind für meine Zwecke PBT_APMRESUMESUSPEND und PBT_APMRESUMESTANDBY interessant. Die erhält meine Anwendung, wenn der Rechner aufgeweckt wird.
Um auf die Power-Management-Nachricht zu reagieren, muss ich dann nur die WndProc()-Methode meines Anwendungsfensters überschreiben:
Public Class Form1
Inherits System.Windows.Forms.Form
...
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_POWERBROADCAST As Integer = &H218
Const PBT_APMRESUMESUSPEND As Integer = 7
Const PBT_APMRESUMESTANDBY As Integer = 8
If m.Msg = WM_POWERBROADCAST Then
If m.WParam.ToInt32 = PBT_APMRESUMESTANDBY Or m.WParam.ToInt32 = PBT_APMRESUMESUSPEND Then
Timer1.Enabled = False
Timer1.Enabled = True
End If
End If
MyBase.WndProc(m)
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
' aktuelle Tarifdaten laden...
End Sub
End Class
Die WndProc()-Methode schaltet dann nur meinen Timer aus und wieder an und schon läuft alles wieder automatisch - bis zur nächsten Schlafenszeit.