PHP Code-Analyse mit Ant

Dieser Beitrag ist Teil 1 von 2 in der Serie Continuous Integration

    Graph LeadIch mag Code. Nicht jeden natürlich, „Douchebag“ Code nervt, Code der aussieht wie ein schreiender Quasimodo oder Code der von einem Dr. Frankenstein in Ausbildung erstellt wurde sind bemitleidenswert und sehr viel Code stinkt. Code, der gefällt (wie beispielsweise der von Frigidor) ist clever ohne zu bluffen, kurz aber nicht kryptisch, tut etwas, ist lesbar und hat einige Wows drin.

    Diese Bewunderung sollte in Zahlen gefasst werden: mit Statistiken, Diagrammen, Balken und Graphen. Dies dient zur Vorbereitung der „Continuous Integration“ mit Jenkins/Hudson, die in dieser Serie behandelt wird. Als Quellen haben vor allem jenkins-php und ein äusserst Lesbarer Artikel im Entwickler Magazin 01/11 gedient.

    Hier wird nun gezeigt, wie mit einem Befehl:

    • Die Unittests durchführt.
    • Qualitative Softwaremetriken mit PHP Depend misst: Hierarchietiefe, Komplexität, …
    • Unschöne Teile mit PHPMD identifiziert.
    • Copy-Paste-Verbrecher aufspührt mit phpcpd.
    • Den Coding-Style prüft mit dem PHP Code Sniffer.
    • Quantitative Softwaremetriken mit phploc misst.
    • Die PHPDoc-Doku erstellt.
    • Die Resultate mit dem PHP Code Browser schön darstellt.

    Weiterlesen

    Individuelle, geschützte und dynamisch erstellte SVN-Repositories

    facesDas Problem: Es sollen individuelle SVN Verzeichnisse erstellt werden. Ein Bereich also, in dem eine Person beliebig viele Repositories erstellen kann, die nur von ihr selbst angesehen und bearbeitet werden können. Mann könnte jetzt meinen – vorallem mit viel jugendlichem Leichtsinn – das sei ein einfaches, allgemein bekanntes Problem. Pustekuchen! Das muss ein ganz fremder wohl ganz abstruser Wunsch sein.

    Verschiedene Dinge schränken die Chance ein, eine einigermassen vernünftige und allgemeine Lösung zu finden 🙁 .Doch der Reihe nach. Was ist gegeben:

    • Ein wunderbarer Server, mit SSL und allem.
    • Das Einrichten eines neuen Users sollte ungefähr 0,01 Sekunden menschlicher Arbeitskraft bedürfen.
    • Die Benutzer sind in einem Active Directory gespeichert.
    • Apache2, PHP, und alles was man will ist installiert.

    Einschränkende Dinge

    Die grösste Peinlichkeit ist die Direktive SVNParentPath (von mod_dav_svn). An sich sollte die Direktive ein Basisverzeichnis für die SVN-Repositories angeben aber:

    • Der Parameter (das Verzeichnis) ist absolut undynamisch. Er nimmt keine Umgebungsvariable, Regexp oder sonst was, sondern ganz stur nur einen festen Pfad.
    • Kann nicht in einer .htaccess Datei gesetzt werden

    Gna! Das ist echt 2002! So wird das nichts mit dynamisch erstellten SVN-Verzeichnissen. Aus reinem Masochismus hane ich verschiedene Lösungen wie mod_rewrite, symbolischen Links, LocationMatch und alles was sonst noch so Zeit kostet ausprobiert. Keine Chance…
    Erschwerend hinzu kommt, dass sich SVN überhaupt nicht mit der Alias Direktive anfreunden kann und konsistent stur auf eine jungfräuliche Location beharrt: Der Versuch wird mit einem Repository moved permanently, please relocate belohnt.

    Die Krönung: mod_authz_path

    mod_authz_path klingt vielversprechend. An Hand des Pfades sollte der User und das Realm festgelegt werden können. Jaaa, aber nicht als Location, sondern nur als Pfad (<Directory> im Apache), wie man mit dem Debuggen feststellen kann:

    AuthNamePathMatch (.+) "Persoenliches SVN $1"
    AuthzUserPathMatch (.+) $1

    Mit dem Pfad findet sich aber – wie schon weiter oben geflucht – das SVN nicht zurecht.

    Ganz nebenbei hat mich dieses Modul etwa viertausend Stunden gekostet. Wenn es geladen wird, funktioniert authnz_ldap nicht mehr richtig. Nicht überhaupt nicht, sondern einfach das require Statement spielt verrückt und verhält sich unberechenbar. Nach langer Zeit habe ich dann das auch gemerkt :irre: . Aber erst nachdem ich unter viel Wehklagen in einer Panik die anderen Server getestet habe, ob die Authentifizierung dort auch nur scheinbar funktioniert.

    Die Lösung

    Nach ein paar Jahren die Lösung: Gutes, altes PHP.

    Auf dem SSL-Port des Servers ist eine Website, auf der sich die User mal als erstes über Apache am Active Directory authentifizieren müssen. Dadurch habe ich Zugriff auf den Benutzername mittels $_SERVER['AUTHENTICATE_SAMACCOUNTNAME']. Loggt sich jemand ein den wir noch nicht kennen, dann:

    1. Wird der svn-Grundpfad erstellt, wenn er noch nicht existiert.
    2. Wird eine Apache-Konfiguration für diesen neuen Grundpfad erstellt, die das ParentPath und den entsprechenden User beinhaltet.
    3. Wird Apache neu gestartet → und der Benutzer währenddessen zum Warten gebracht. Das ist leider notwendig zum Einlesen der Konfigurationserweiterung.

    Die neue Konfigurationsdatei wird „lokal“ erstellt und in der Grundkonfiguration /etc/apache2/sites-available/isvn.myhost.ch eingebunden:

            Include /data/www/isvn.myhost.ch/config/svn.conf

    PHP Code für die neuen Benutzer:

      $svn_user=strtolower($_SERVER['AUTHENTICATE_SAMACCOUNTNAME']);
      if(!$svn_user) { echo "Hacker, you will be disintegrated!"; exit(1); }
      $svn_root=SVNROOT."/".$svn_user;
      if(!is_dir($svn_root)) { // NEW USER
            // Make svn Parent Path
            mkdir($svn_root);
            // Write Config
            $fh = fopen(SVNCONF, 'a') or die("can't open file");
            $svnDirective =<<<EOD

    <Location /svn/lp/$svn_user>
            DAV svn
            SVNParentPath /data/svn/lp/$svn_user
            AuthName "Persoenliches SVN von $svn_user"
            AuthType Basic
            AuthBasicProvider ldap
            AuthLDAPBindDN ldapanonymous@myhost.local
            AuthLDAPBindPassword g3h31M3Sp+
            AuthLDAPUrl ldap://adc1.myhost.local/ou=Accounts,dc=myhost,dc=local?sAMAccountName?sub
            require ldap-user $svn_user
    </Location>

    EOD
    ;
            fwrite($fh, $svnDirective);
            fclose($fh);

            // Make th User wait
            echo "<html><head><title>SVN wird eingerichtet</title>\n";
            echo '<meta http-equiv="refresh" content="5; URL=https://isvn.myhost.ch">'."\n";
            echo "</head><body>".implode(" ", explode(".",$_SERVER['AUTHENTICATE_SAMACCOUNTNAME'])).": Ihr SVN Grundverzeichnis wird erstellt. Bitte warten Sie.</body></html>";

            // Restart Apache
            exec('echo "/usr/bin/sudo /etc/init.d/apache2 restart" | /usr/bin/at now');
            exit();
      }

    Damit der User www-data den Apache restarten darf, muss ihm das per /etc/sudoers erlaubt werden:

    www-data ALL=NOPASSWD: /etc/init.d/apache2

    Dass die Berechtigungen auch für alle benötigten Zusatzdateien reichen, machen wir den Trick mit dem at.

    Somit haben wir also eine hochindividuelle Konfigurationsdatei für alle User und Ihr SVN Grundverzeichnis. Die PHP-Seite ermöglicht es, SVN-Repositories anzulegen und zeigt auch gerade den annektierten Platz an:

    Die SVN-Steuerkonsole

    Die SVN-Steuerkonsole

    Die Quellen

    Hier sind die PHP-Quellen. Für weitere Details stehe ich gerne zur Verfügung.

    Profiling des Spielagenten mit Eclipse TPTP

    Die FAQ zu Avaloqix geben ganz klar die Kriterien bekannt:

    Für den Wettbewerb werden Graphen mit 20-60 Knoten genutzt. Pro Knoten gibt es max. 9 Kanten. Zusätzlich gibt es eine Timelimite von 5 sec pro Spielzug.

    Leider fehlen die Angaben über die Leistungsfähigkeit der Wettbewerbsmaschine. Ich gehe mal ganz willkürlich davon aus, dass sie nicht langsamer ist oder weniger Speicher hat als mein Laptop und die Resultate nur besser sein können. Eine Geschwindigkeitsoptimierung dürfte also nicht ganz sinnlos sein. Ausserdem wird mein Agent in den ersten paar Zügen nie fertig mit Rechnen und muss abbrechen. Eine Optimierung die hier mehr Spielzüge erlaubt, würden die Gewinnchancen signifikant steigern.

    Ausserdem gilt wie überall im Leben: Machs schön und richtig und erst danach schnell.

    Benchmarking

    Bei einer Geschwindigkeitsoptimierung sollte man immer benchmarken. Das heisst: Die Geschwindigkeit vor und nach der Optimierung nachmessen, so dass man sicher sein kann, dass man in die richtige Richtung optimiert hat. Ebenfalls empfehlenswert ist gezieltes optimieren: Wenn man eine Funktion nur ein bisschen verschnellert, die sehr oft aufgerufen wird, bringt das unter Umständen mehr wie wenn man eine Funktion mit viel Aufwand drastisch verkürzt die nur wenige Male zum Zuge kommt. Man braucht also Daten.

    Als Benchmark habe ich – mangels anderer Gegner – ein Spiel gegen den OptimalOneAgent gewählt. Für den Benchmark sollten immer die gleichen Einstellungen gelten, die komfortable Avaloqix Oberfläche erlaubt zum Glück das Festlegen des Spielfeldes mit dem Random Seed.

    Es empfiehlt sich auch, Turn Pause zu deaktivieren und End when winner is ovious zu aktivieren.

    Profiling

    Doch woher die Daten nehmen und nicht stehlen? Dafür zuständig sind sogenannte Profiler. Für Eclipse gibt es einen (einer der versteckten Diamanten) im TPTP-Projekt.
    Weiterlesen

    Avaloqix: Unterhaltsamer Java-Programmierwettbewerb

    Flow im HirnIch liebe Wettbewerbe und ich liebe das Spielen. Meine inneres, urmännliches Ich springt voll auf das kompetitive Element solcher Veranstaltungen an. Zum Erlegen von Höhlenbären oder für Sportwettkämpfe eigne ich mich definitiv nicht, darum bin ich froh, wenn ab und zu ein Kräftemessen auf meinem – eher wetwarelastigen – Gebiet stattfindet.

    Leider hat es jetzt seit längerer Zeit kein Codeduel (ein spassiger, von Microsoft Schweiz gesponserter/organisierter Event in welchem SOAP-Services gegeneinander angetreten sind, für den es aber leider keine offizielle Website mehr gibt) mehr gegeben und für die kommerzielle Version solcher Wettbewerbe habe ich im Moment wirklich keine Zeit.

    Über die Seite des Schweizerischen Jahres der Informatik bin ich auf Avaloqix gestossen und war sofort begeistert. Am Tag der Informatik am 29. August 2008 in Zürich wird es ein Event speziell zu diesem Wettbewerb geben.

    Das Spiel

    Es geht darum, einen Spieler für eine abgewandelte Version des Shannon Switching Games zu programmieren. Kurz erklärt: Es gibt eine Quelle und ein Senke, die über ganz viele Röhren und Sammelpunkte miteinander verbunden sind. Im ersten Durchgang versucht der eine Spieler einen möglichst grossen Durchfluss zu erzielen indem er Röhren freischaltet, während der andere Spieler ebendies durch das Ausbauen von Röhren zu verhindern versucht. In einer zweiten Runde werden die Rollen der Spieler in demselben Röhrengeflecht getauscht. Gewonnen hat, wer als Durchflussmaximierer den grösseren Durchfluss erzielen konnte.
    Weiterlesen

    AntMe, Spass mit programmierbaren Ameisen

    AntMe!Nachdem ich die Ameisen in meiner Wohnung mit ganz perfidem tragts-es-zu-euch-nach-Hause-und-vergiftet-eure-Nachkommen-Gift losgeworden bin, habe ich sehr viel sympathischere Zeitgenossen dieser Spezies getroffen: AntMe ist ein Programmierspiel, dass aus Microsofts Coding4Fun Initiative (nicht zu verwechseln mit dem Galileo Coding for Fun Buch. Das ist auch spassig, behandelt auch AntMe, aber zusätzlich noch viele Andere Dinge) hervorgegangen ist. Es hat einen eigenen Wikipedia Artikel und vorallem eine geniale Homepage.

    Grundsätzlich geht es darum Ameisen zu implementieren die überleben, Äpfel und Zucker sammeln und unter Umständen auf Käfer und fremde Ameisen losgehen. Ein Markierungsmechanismus sorgt dafür, dass die Ameisen untereinander kommunizieren können. Fortgeschrittene Ameisenpapis und -mamis können spezialisierte Ameisen erstellen und RPG mässig Eigenschaften verbessern, wenn sie dafür andere verschlechtern.

    Die Ameisen in Aktion

    Die Ameisen können in C# oder in Visual Basic .net mit der notwendigen Intelligenz versehen werden. Bestimmte Methoden werden zu bestimmten Ereignissen aufgerufen; beispielsweise WirdMüde() (ja, mit „ü“ und deutsch) oder SiehtFeind(ByVal käfer As Käfer). Zur Steuerung steht fast der ganze .net Sprachumfang zur Verfügung, sowie Ameisenspezifische Hilfsmethoden wie etwa GeheZuBau() oder GreifeAn(käfer).

    Quickstart

    Auf der AntMe Homepage findet man verschiedene Versionen. Wir spielen hier mit der 1.5 Beta 2 (VB.net oder C#, je nach Gusto). Die Profiversion beinhaltet den gesamten Quellcode der Spielengine (!). Das ist so genial, dass es nochmals erwähnt werden darf: Die Profiversion beinhaltet den gesamten Quellcode der Spielengine. Will man sich nur mit den Ameisen beschäftigen, reicht die Einsteigerversion. Im Download enthalten ist ein gut dokumentiertes Ameisenskelett, bei dem sofort losgottgespielt werden kann.
    Eine gute Doku über die Klassen und deren Methoden sowie ein Tutorial wird mitgeliefert. Zum Teil beziehen sich die Unterlagen noch auf Version 1.1, aber der Transfer zu 1.5 ist eigentlich kein Problem. Für Leute, die lieber kuckn statt lesen, gibt es gut gemachte Screencasts, die ebenfalls in die Thematik einführen.

    Für eher haptisch veranlagte Menschen gibt es auch ein Buch: AntMe! – Programmieren und Spielen mit den Ameisen und Visual C#. Das kenne und besitze ich allerdings (noch) nicht.

    Verfügt man noch über keine Programmierumgebung, so könn bei Microsoft gratis die Express Versionen verschiedener Sprachen downgeloaded werden. Für die 3D Visualisierung braucht man
    noch DirectX. Mit der neusten Version (nicht mit der im Forum angegebenen) hat es wunderbar gefunzt.

    Turniere

    Ein Bisschen lebt das Ganze auch vom kompetitiven Element (um ehrlich zu sein, vielleicht ist ein Bisschen etwas untertrieben: ICH FRESS EUCH ALLE AUF!). In der Version 1.5 funktioniert das Importieren von anderen Ameisen leider nicht und es wird eine unbehandelte Ausnahme ausgespuckt:
    Der Typ für Member AntMe.SpielKonfiguration.AntMe.Simulation, Version=1.5.0.0, Culture=neutral, PublicKeyToken=37d8e32ef3294969 wurde nicht aufgelöst.

    Workaround für die VB-Version (C# ungetestet):
    Weiterlesen

    Selbst einen Flickr Screensaver in VB.net programmieren

    ScreensaverJa, 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.ToInt32, Win32Corners.GWL_STYLE)
                style = style Or Win32Corners.WS_CHILD
                SetWindowLong(f.Handle.ToInt32, Win32Corners.GWL_STYLE, style)
                SetParent(f.Handle.ToInt32, previewHandle)
                SetWindowLong(f.Handle.ToInt32, Win32Corners.GWL_HWNDPARENT, previewHandle)
                SetWindowPos(f.Handle.ToInt32, 0, r.left, 0, r.right, r.bottom, Win32Corners.SWP_NOACTIVATE Or Win32Corners.SWP_NOZORDER Or Win32Corners.SWP_SHOWWINDOW)
            End Sub

            Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As ApplicationServices.StartupEventArgs) Handles Me.Startup
                'Dunno why this is needed
                My.Settings.RunsAsPreview = False
                If e.CommandLine.Count > 0 Then
                    ' Aus zwei Zeichen bestehendes Befehlszeilenargument abrufen
                    Dim arg As String = e.CommandLine(0).ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim().Substring(0, 2)
                    Select Case arg
                        Case "/c"
                            ' Dialogfeld "Optionen" anzeigen
                            Me.MainForm = My.Forms.OptionsForm
                        Case "/p"
                            My.Settings.RunsAsPreview = True
                            Me.MainForm = My.Forms.ScreenSaverForm
                            SetForm(Me.MainForm, e.CommandLine(1))
                        Case "/s"
                            My.Settings.RunsAsPreview = False
                            Me.MainForm = My.Forms.ScreenSaverForm
                        Case Else
                            MessageBox.Show("Ungültiges Befehlszeilenargument:" + arg, "Ungültiges Befehlszeilenargument", MessageBoxButtons.OK, MessageBoxIcon.Error)
                    End Select
                Else
                    ' Wenn keine Argumente übergeben wurden, Bildschirmschoner anzeigen
                    Me.MainForm = My.Forms.ScreenSaverForm
                End If

            End Sub

            'OnInitialize wird für die erweiterte Anpassung des eigenen Anwendungsmodells (MyApplication) verwendet.
            'Der Startcode für Ihre spezielle Anwendung sollte in einem Startereignishandler positioniert werden.
            <Global.System.Diagnostics.DebuggerStepThrough()> _
            Protected Overrides Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean
                Return MyBase.OnInitialize(commandLineArgs)
            End Function

    Installation

    Im bin/Release/ Verzeichnis gibt es nach dem Erstellen des Projektes mehrere Dateien. Damit die Geschichte läuft, muss man FlickrSaver.exe in FlickrSaver.scr und FlickrSaver.exe.config muss in FlickrSaver.scr.config umbenamsen. Danach können die 3 Dateien:

    • FlickrSaver.scr
    • FlickrSaver.scr.config
    • FlickerNet.dll

    ins c:\WIndows\system32 Verzeichnis verschoben und als Bildschirmschoner aufgerufen werden.

    Viel Spass mit dem Code