Ja, ich lebe in einer dunklen Welt: in diese Welt sind die memory_limits noch auf 8MB, viele Pixel auf dem Monitor sind untot, Slashdot ist wegen zuviel Arbeit nicht mehr in der History meiner Browserleiste und aus hier nicht näher zu spezifizierenden Gründen muss ich mit Visual Basic 2005 Express Edition arbeiten. Und das in einer VMWare Umgebung unter Linux. Da kommt Freude auf!
Nundenn, da sucht man sich doch einfach mal ein nettes Projekt um den Einstieg etwas zu erleichtern. Meine Wahl fiel auf einen Bildschirmschoner, der in regelmässigen Abständen Bilder von flickr darstellen soll.
Benötigte Dinge
Um möglichst schmerzfrei auf Flicker zugreifen zu können, benutze ich die FlickrNet API Library von Codeplex. Diese Library ist relativ gut dokumentiert und hat einen irgendwie offiziellen Touch.
Ausserdem benutze ich das Screensaver Starter Kit, das bei Visual Basic dabei ist um das grundlegende Gerüst zu erzeugen. Es kann angewählt werden, wenn ein neues Projekt erstellt wird.
Lustige Details und Probleme
Generell kann man mal ganz viel aus dem generierten Code löschen. Interessierte seien auf das beiliegende Projekt verwiesen.
FlickrNet API Library
Zuerst muss die FlickerNet.dll in das Projekt kopiert werden. Neuere Versionen haben bei mir Probleme gemacht, was aber gar nichts heissen muss. Damit alle diese Fehler ausgeschlossen werden können, habe ich eine aus einem funktionierenden Beispiel herauskopiert. Bei My Project → Verweise muss diese DLL hinzugefügt und der Namespace importiert werden.
Bei Flickr muss noch einen API-Key gelöst werden und dann steht dem Einsatz nichts mehr im Wege.
Bildschirmschoner
Ein Screensaver ist nichts anderes als ein grosses Form ohne Fensterränder, das sich immer nach vorne zwängt und den Mauszeiger abschaltet. Ein Bildschirmschoner sollte drei Modi unterstützen:
- Darstellen
- Preview beim Windows-Konfigurationsdialog
- Einstellungen
Die Hauptarbeit, nämlich die Darstellung des eigentlichen Screensavers wird im ScreenSaverForm gemacht, Die Einstellungen werden über das OptionsForm abgehandelt.
Mit der folgenden Funktion wird die URL von ca. 100 Fotos im Feld allPhotos gespeichert:
''' <summary>
''' Lädt die URLs von einigen Bilder zum gegebenen Tag von Flickr in ein Feld
''' </summary>
Private Sub LoadBackgroundImage()
Dim f As Flickr = New Flickr(apikey)
Dim options As PhotoSearchOptions = New PhotoSearchOptions()
options.Tags = My.Settings.Tag
Dim bgPhotos As Photos = f.PhotosSearch(options)
allPhotos = bgPhotos.PhotoCollection
currentImageIndex = 0
End Sub
Ein Timer erhöht den Index des aktuell darzustellenden Bildes:
Private Sub backgroundChangeTimerTick(ByVal sender As Object, ByVal e As EventArgs) Handles backgroundChangeTimer.Tick
' Hintergrundbild ändern und nächstes Bild verwenden.
currentImageIndex = currentImageIndex + 1
Refresh()
End Sub
Dieses Codesnipped lädt ein Bild mit einem Index herunter uns stellt es dar:
Protected Overrides Sub OnPaintBackground(ByVal e As PaintEventArgs)
If allPhotos.Length > 0 Then
currentImageIndex = currentImageIndex Mod allPhotos.Length
' Aktuelles Hintergrundbild gestreckt zeichnen, sodass es den gesamten Bildschirm ausfüllt
Dim f As Flickr = New Flickr(apikey)
Dim currentImage As Image
currentImage = Image.FromStream(f.DownloadPicture(allPhotos.Item(currentImageIndex).MediumUrl))
e.Graphics.DrawImage(currentImage, 0, 0, Size.Width, Size.Height)
Else
myError = "No Photos for this Tag"
End If
End Sub
Weiter werden Tastatureingaben und Mausbewegungen abgefangen. Wenn der Screensaver nicht im Previewmodus ist, beendet er sich bei einer Benutzerreaktion.
Das Preview
Beim Screen Saver Starter Kit fehlt das Preview. Irgendwo haben die Hersteller geschrieben, das sei Absicht, damit die Entwickler das erweitern könnten. <laut>Haha!</laut>. Der Grund wurde mir dann schon klar. Bei einem Aufruf im Preview-Modus wird ein Windowshandle des Elternfensters mitgegeben. Da muss man sich als Kind eintragen und das geht mit dem super .net nicht so einfach. Dank VB-Helper und msdner (leider offline: http://www.msdner.com/dev-archive/6/10-32-68963.shtm) habe ich dann eine Lösung gefunden und implementieren können.
Der grösste Teil dieses magischen Codes steht in MyEvents.vb. Um das zu sehen muss man im Menü Projekt → Alle Dateien anzeigen auswählen und den Ast My Project aufklappen. Dort drin befindet sich die Unterscheidung der Modis, etc:
Public Declare Function GetClientRect Lib "user32" (ByVal hwnd As Integer, ByRef lpRect As RECT) As Integer
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer) As Integer
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Integer, ByVal nIndex As Integer, ByVal dwNewInteger As Integer) As Integer
Public Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Integer, ByVal hWndInsertAfter As Integer, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal wFlags As Integer) As Integer
Public Declare Function SetParent Lib "user32" (ByVal hWndChild As Integer, ByVal hWndNewParent As Integer) As Integer
Public Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Public Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
Public Class Win32Corners
Public Const SWP_NOACTIVATE As Object = &H10
Public Const SWP_NOZORDER As Object = &H4
Public Const SWP_SHOWWINDOW As Object = &H40
Public Const GWL_STYLE As Object = -16
Public Const WS_CHILD As Object = &H40000000
Public Const GWL_HWNDPARENT As Object = -8
Public Const HWND_TOP As Object = 0
End Class
Private Sub SetForm(ByRef f As ScreenSaverForm, ByRef arg As Long)
Dim style As Integer
Dim previewHandle As Integer = Int32.Parse(CType(arg, String))
Dim r As New RECT
GetClientRect(previewHandle, r)
With f
.WindowState = FormWindowState.Normal
.FormBorderStyle = FormBorderStyle.None
.Width = r.right
.Height = r.bottom
End With
style = GetWindowLong(f.Handle.