BigBasti's Blog About Me & my Digital Lifestyle

26Jul/093

VB: Ordnerstrukturen einlesen Teil 2

In dem ersten Teil dieses Tutorials haben wir eine Ordnerstruktur bzw. einen Ordnerbaum in eine Treeview mit Hilfe einer Rekursiven Methode in eine TreeView geladen. Dabei haben wir festgestellt, dass das wunderbar funktioniert, aber sehr lange dauert, und den Anschein erwecken könnte, dass das Programm abgestürzt ist, da es nicht mehr auf Eingaben reagiert!

Am Ende des ersten Teils haben wir uns also die Frage gestellt, wie macht es der Windows Explorer, dass er die Ordnerstrukturen so schnell einließt? Um diese Frage zu beantworten muss man den Windows Explorer (ab nun WE) mal etwas genauer beobachten.

Ich wette ihr kennt alle folgende Situation. Ihr startet den WE und seht eure Ordner meißt sofort, nun öffnet ihr einen Unterordner (von dem Ihr wisst, dass dieser viele Daten enthält) und müsst feststellen, dass der WE nun einen Moment braucht um dessen Inhalt anzuzeigen!

Ein weiteres Beispiel ist, dass der WE manchmal das "plus"-Zeichen für Ordner-Aufklappen anzeigt, aber wenn man darauf klickt, verschwindet dieses "plus"-Zeichen wieder. Was daran liegt, das der WE nun feststellt (allso erst beim Klicken), dass der Ordner garkeine weiteren Unterordner hat!

Diese "Phänomene", besonders das erste, tauchen sehr oft nach dem frischen Start von Windows auf!

Was kann man also aus dieser Beobachtung schließen? Ganz einfach, dass der WE nicht gleich den Ganzen Ordnerpfad lädt. Denn würde er das tun wäre er viel langsamer, zb so wie unser Programm aus Teil 1!

Wenn man über dieses Vorgehen nachdenkt wird man feststellen, dass es genial ist! Warum soll ich alle Ordner einlesen, was zeitlastig ist, wenn ich nur einen Ordner einlesen kann und dann erstmal abwarte, wohin der Benutzer dann will um diesen Ordner dann erst zu laden.

Oder auch eine zweite Variante ist möglich. Man lädt erstmal den Ersten Ordner ein und zeigt diesen dem Benutzer an. Dadurch kann der Benutzer schon mal sein weiteres Vorgehen planen. Und während der Benutzer nachdenkt welchen Ordner er als nächstes öffnen soll lädt das Programm im Hintergrund die restliche Ordnerstruktur nach.

In diesem Beispiel möchte ich auf diese (zweite) Variante eingehen und unser Beispiel aus Teil 1 so erweitern, dass es genauso arbeitet! (Ihr könnt als Vorlage den Code aus Teil 1 verwenden)

Also unser Vorgehen nochmal zusammengefasst:

1. Den vom Benutzer gewählten Ordner einlesen (nur erste Ebene!)

2. Die eingelesene Ebene anzeigen

3. Im Hintergrund die restlichen Unterordner nachladen

Schritt 3 ist hierbei der komplizierteste, denn wir müssen dafür sorgen, dass der Benutzer Eingaben tätigen kann, aber das Programm weiter die Ordner nachlädt, also zwei dinge gleichzeitig tun! - Wie machen wir das?

Dieses Problem werden wir lösen, in dem wir den Code für Schritt 3 in einen separaten Thread auslagern der läuft dann neben dem Form1-Thread, der die Benutzereingaben verarbeitet!

Das heißt wir müssen unser altes Programm um zwei neue Funktionen erweitern, nämlich um die Funktion die ersteinmal Schritt 1 & 2 erledigt und dann die zweite neue Funktion in einem neuen Thread startet damit diese dann Schritt 3 erledigen kann!

Hier die neuen Funktionen:


    Sub getAllSubFolders()
        'Alle Ordner aus dem gewählten Pfad auslesen
        For Each s As IO.DirectoryInfo In _
          New IO.DirectoryInfo(path).GetDirectories
            t.Nodes.Add(s.Name)
        Next
        'Einen neuen Thread erstellen der
        'alle restichen Verzeichnisse ausließt
        Dim a As New Threading.Thread(AddressOf startTheSubDir)
        a.Start()
    End Sub

    Sub startTheSubDir()
        For Each tn As TreeNode In t.Nodes
            Try
                'nun lassen wir den Thread die Unterordner
                'von allen Verzeichnissen in der Root-Note t
                'einlesen und zu t hinzufügen
                TreeView1.Invoke(New getFoldersFromPathDel _
                  (AddressOf getFoldersFromPath), _
                    New Object() {tn.FullPath, tn})
            Catch ex As Exception
                'Es kann die UnauthorizedAccessException Auftreten!
                'Diese wird hier abgefangen soadass nur
                'Ordner eingelesen werden die für das
                'Programm freigegeben sind (nur Vista)
                'alle Fehler speichern
                errors = errors & vbNewLine & ex.Message
            End Try
        Next
        MessageBox.Show(errors)
    End Sub

    'Diese Funktion ist der Delegate Aufruf von getFoldersFromPath
    'Diese Wird benötigt um auf den Inhalt von TreeView1 zuzugreifen
    'Da die TreeView1 von einem anderen Thread erstellt wurde kann nur dieser
    'Darauf zugreifen! Durch die Invoke-Methode können wir dieses Problem umgehen
    Public Delegate Function getFoldersFromPathDel _
      (ByVal start As String, ByVal tv As TreeNode)

Wie man sieht wird ersteinmal die Funktion getAllSubFolders aufgerufen, die dann den ersten Ordner einließt und dann einen neuen Thread mit der Funktion startTheSubDir startet.

Noch bevor der neue Thread gestartet ist sieht der Benutzer seinen gewählten Ordner und dessen Unterordner, die er aber noch nicht erweitern kann. Nun im Hintergrund werden die einzelnen Ordner nachgeladen und die einzelnen "plus"-Zeichen tauchen an den Ordnern auf, die erweiterbar sind!

Dafür sorgt der Thread der nebenbei läuft, und über die Delegierte Funktion getFoldersFromPathDel die rekursive Funktion aus Teil 1 aufruft!

An dieser Stelle ist es wichtig zu wissen, warum man hier delegieren muss. In VB ist es so ein Steuerelement (zb. eine TreeView) kann nur von dem Thread bedient werden, der dieses auch erstellt hat. Will man nun aber aus einem anderen Thread auf dieses Steuerelement zugreifen muss man dieses ersteinmal mit der Funktion bekannt machen! Das ist was Delegate macht!

Man merkt sofort, dass diesmal das Programm sehr viel schneller den selben Ordnerpfad einlesen kann als noch aus Teil 1. Man hat sofort etwas womit man weiterarbeiten kann, ohne ersteinmal warten zu müssen, bis alles fertig ist!

Dieses Vorgehen ist aber immernoch nicht das optimalste! Besser wäre es doch für jeden Ordnerpfad der in Schritt 1 eingelesen wird einen eigenen Thread zu starten der dann nur die Unterordner zu dem einen Ordner einließt! Denn so wie es nun hier ist muss der eine Thread die ganze Arbeit machen und ist unterm Strich nur wenig schneller als der aus Teil 1!

Da dieser Beitrag sonst zu lang geworden wäre werde ich wohl doch noch einen Teil 3 machen wo ich auch auf die eben beschriebene Methode eingehe! Wenn bei euch Interesse besteht!

Bitte teilt mir auch eure Meinung & Vorschläge per Comment oder Mail mit.

Dieses Beispiel herunterladen: Download

23Jul/091

VB: Ordnerstrukturen einlesen Teil 1

In diesem ersten Teil (von zwei) geht es darum wie man einen Ordner (inklusive aller Unterordner) in ein Treeview laden kann. In dem ersten Teil werden wir eine kleine Demoapp erstellen, die Den Ordnerbaum rekursiv einließt.

Jeder kennt den Windows Explorer mit der Ordneransicht auf der linken Seite, die durch Linien mit einander verbunden sind. Wenn man selbst Programme schreibt kommt man oft an die Stelle, wo man ebenfalls ein solchen Ordnerbaum erstellen muss oder einen anderen einlesen und verarbeiten.

Da VB dafür noch keine Funktion bereitstellt muss man das noch selber machen, in diesem Beispiel werde ich euch zeigen wie man das mit wenig Aufwand erledigen kann.

Wie es schon im ersten Absatz steht werde ich dafür eine rekursive Methode benutzen. Eine rekursive Methode ist eine Methode die sich selbst aufruft. Solche Methoden werden sehr oft in Verbindung mit Ordnern und Listen eingesetzt.

Hier zunächst meine Funktion:


    Function getFoldersFromPath(ByVal start As String, _
      Optional ByVal tv As TreeNode = Nothing)
        'Start = Der Ordner den wir in die TreeView laden sollen
        'tv = Die aktuelle TreeNode an der wir arbeiten

        'Jeden ordner in dem übergebenen Pfad (start) bearbeiten
        For Each s As IO.DirectoryInfo In _
          New IO.DirectoryInfo(start).GetDirectories
            'Einen neuen Node erstellen
            'n ist jeweils immer der Kern Node
            Dim n As New TreeNode(s.Name)
            'Nun sich selbst aufrufen und als aktuellen Node
            'sich selbst (n) übergeben
            getFoldersFromPath(s.FullName, n)
            'Nach dem der Node n inkl. aller Unterverzeichnisse
            'eingelesen wurde diesen unserem hauptnove tv übergeben
            tv.Nodes.Add(n)
        Next
        'Nun den Note mit allen eingelesenen Ordnern zurückgeben
        Return tv
    End Function

Auch wenn diese Funktion nur aus 7 Zeilen besteht hat sies dennoch insich. Denn es ist gar nicht so einfach sich da reinzudenken!

Gehen wir diese Funktion mal durch. Die Funktion erwartet zwei Parameter zu Beginn. Einen String, der den Pfad angibt, aus dem die Ordner ausgelesen werden soll und eine TreeNode zu der die ausgelesenen Ordner hinzugefügt werden sollen.

Der zweite Parameter ist mit dem Schlüsselwort Optional markiert, was bedeutet, dass dieser Parameter beim Aufrufen der Funktion nicht übergeben werden muss!

In Zeile 6 - 17 lesen wir den übergebenen Ordner (aus der Variable start) aus und nehmen seinen Inhalt durch.

Das "Geheimnis" dieser Funktion liegt in Zeile 13 dort wird der Rekursive Aufruf gestartet und als Parameter der aktuell zu bearbeitende Ordner (s) und der aktuelle Baum (n). Nach diesem Aufruf wird dieselbe Funktion nochmals gestartet und läuft den Ordner n durch und startet sich dabei ggf. nochmal selbst falls n auch Unterordner enthält!

So könnte es aussehen, nach dem ein Ordner eingelesen wurde:

treeview01

Wie man sieht wurde der ganze ordner eingelesen und als Baum in der TreeView dargestellt. Nach Belieben kann man nun noch Verschönerungen vornehmen, wie zb Ordner-Icons hinzufügen aber das ist ein anderes Thema!

Leider hat diese Methode einen entscheidenden Nachteil, nämlich die Performance! Solange die Ordner nicht allzugroß sind funktioniert diese Methode super! (ca solange es < 1000 Ordner sind)

Aber versucht man mal den ordner "C:\" einzulesen, also den gesamten Ordnerverlauf der Partition C: so könnte es schon mal ein oder zwei Minuten dauern! Abhängig von dem Umfang der Ordnerstruktur auf eurer C-Partition!

Wie macht man das nun also, dass die Ordner schnell eingelesen werden, zb so wie im Windows Explorer, der nur wenige Augenblicke dafür benötigt! - So scheint es zumindest!

Mit dieser Thematik werden wir uns in dem zweiten Teil dieses Beitrags beschäftigen!

Dieses Beispiel herunterladen: Download

22Jul/091

VB: Windows Herunterfahren verhindern

Wer eine einfache Möglichkeit sucht mit wenig Code zu verhindern, dass ein Benutzer Windows herunterfährt für den habe ich hier etwas.

Eigentlich ist es eher ein Programmier-Fehler, wenn die eigene Anwendung verhindert, dass Windows herunterfahren kann. Aber wer weis, vielleicht kann das auch mal nützlich sein!

Das Vorgehen hier ist relativ simpel. Alles was man tun muss ist zu verhindern, dass euer Hauptfenster des Programms, dessen Schließung zum Beenden des Programms führt geschlossen wird.

Das Schließen des Fensters kann man in dem "FormClosing"-Event abfangen etwa so:


Private Sub frmMain_FormClosing(ByVal sender As Object, _
  ByVal e As System.Windows.Forms.FormClosingEventArgs) _
  Handles Me.FormClosing 

  e.Cancel = True
  Me.Hide()
End Sub

Wenn das Formular nun geschlossen wird fangen wir das ab und beenden dieses Vorgehen. Im Anschluss wird noch das eigene Formular versteckt.

Der Prozess läuft in diesem Fall natürlich weiter und kann in diesem Fall nurnoch über den Taskmanager geschlossen werden!

Windows kann nun auch nicht runterfahren, da das Programm es aus der Bahn wirft! Andere Programme werden natürlich geschlossen. Aber Herunterfahren sollte der Rechner nicht!

Nun stellt sich dich die Frage, was ist wenn ich diese Funktion (Form verstecken statt schließen) nutzen will aber trotzdem will, dass Windows normal herunterfahren kann und mein Programm beendet?

Wenn ihr diesen Code in eurem Programm nutzen wollt, aber nciht wollt dass Windows sich an eurem Programm aufhängt müsst Ihr noch eine Abfrage einbauen, die prüft wer das Fenster eigentlich schließen will:


Private Sub frmMain_FormClosing(ByVal sender As Object, _
  ByVal e As System.Windows.Forms.FormClosingEventArgs) _
  Handles Me.FormClosing 

  If Not e.CloseReason = CloseReason.WindowsShutDown Then
    e.Cancel = True
    Me.Hide()
  End If
End Sub 

Die Abfrage der Funktion CloseReason liefert den Grund für das schließen des Fensters. Wenn der Grund "WindowsShutDown" ist soll der Schließen-Vorgang nicht unterbrochen werden, damit Windows herunterfahren kann.

Alle Anderen Gründe werden abgefangen und das Fenster nur versteckt!

21Jul/090

Sehr beeindruckend: WolframAlpha

Egal welche News-Seite man aufschlägt wird man Schlagzeilen finden die sich mit den Internet-Suchmaschinen befassen! Was mich allerdings wundert, ist dass keine Sau über WolframAlpha schreibt!

bild-112

Ich habe schon vor einer weile die Schule verlassen, kann mich aber noch sehr gut an den Mathe LK erinnern, und die unzähligen Stunden mit rechnen, Formen auf und umstellen usw.

Heute braucht man nur Internet! Denn geht man auf WolframAlpha kann man sein Hirn komplett abschalten! Fragt es was ihr wollt, egal ob wie das Wetter in Berlin war als Britney Spears geboren wurde oder wie der aktuelle Wechselkurs einer Aktie ist - WolframAlpha weiß alles!

Probiert es selbst aus es ist einfach genial! - Hier ein paar Beispiele:

Integrale Bilden

Währungen umrechnen

Wetterdaten abfragen

Informationen zu Chemischen Formeln

Kalorien und Lebensmittelrechner

Und noch mehr, man bekommt nicht nur das Ergebnis angezeigt, sondern auch tonnenweise weitere Informationen angezeigt, die dazu passen! Und das Wichtigste: es funktioniert!

Ausprobieren!

14Jul/090

Microsofts Visionen werden Langsam Wirklichkeit

Microsoft ist ja berühmt dafür, in unregelmäßigen Abständen Videos mit ihren eigenen Zukunftsvisionen zu veröffentlichen. Meißt sind auch Angaben dabei wie lange es dauern wird, bis wir oder "die" (Microsoft) diese Visionen verwirklicht haben.

Erst vor ein paar Monaten hat Microsoft ein Video Veröffentlicht, dass zeigen soll wie unser Leben in der Zukunft aussehen soll. Aber darum gehts jetzt nicht, denn ich will jetzt lieber einen kleinen Rückblick in das Jahr 2005 starten, wo Microsoft ein Video Veröffentlicht hat, das beschreibt wie Zusammenarbeit in der Zukunft (damals in 2010) aussehen wird:

Warum ich jetzt darauf zu sprechen komme? Ganz einfach, denn vor wenigen Tagen hat Microsoft Videos veröffentlicht, die die Verbesserungen von Office in der Version 2010 zeigen. - So ein Zufall oder?

Und erstaunlicherweise sind viele der Punkte die man in dem Video sieht tatsächlich Bestandteil von Office 2010.

So können zb. mehrere Personen gleichzeitig an einem Dokument arbeiten, egal ob Office auf dem Rechner installiert oder nicht, es genügt ein Browser!

Präsentationen können von jedem Ort auf der Welt gehalten werden und auch hier genügt ein Browser zum Vorführen oder zum anschauen der Präsentation!

Dateien sind für alle live zugänglich und Änderungen sehr einfach einsehbar (siehe OneNote 2010).

Lediglich die Sache mit den Webcams ist zur Zeit noch nicht so ohne weiteres möglich! Aber das wird sicher auch noch!

Ich persönlich freue mich schon auf der neue Office und hoffe Microsoft wird auch eine vernünftige Mac-Version bereitstellen!

8Jul/090

Blog ist nun iPhone kompatibel

Die iPhone Benutzer werden ja immer mehr und ich bin inzwischen auch einer von ihnen geworden!

img_0058Und da ich in den Statistiken schon länger beobachte, dass immer mehr Leute mit dem iPhone den Blog aufrufen habe ich nun etwas Anpassung betrieben und ein tolles Plugin gefunden, dass mir die ganze Arbeit abnimmt! :)

Wers mal testen will einfach den Blog vom iPhone aus aufrufen!

veröffentlicht unter: Allgemein, iPhone, Tipps & Tricks keine Kommentare
7Jul/090

Die ersten nicht abwärts kompatiblen iPhone 3GS Apps sind da

Nun ist es also soweit, es tauchen die ersten Apps auf, die nur mit dem neuen iPhone 3G S kompatibel sind!

Es war eigentlich klar, dass es bald inkompatible Programme geben wird, denn zu groß sind die Hardware Unterschiede der beiden Geräte!

Der größte Unterschied, wird wohl der Kompass sein, der überhaupt nicht in dem alten iPhone auftaucht. Nun ist eine App im Appstore aufgetaucht, die genau dieses Feature des neuen iPhones auf eine lustige Art nutzt!

"Metal Detector" heißt das App und ist im App Store für 0,79 Eur zu haben.

bild-111

Und glaubt man den Userbewertungen, scheint das ganze auch zu funktionieren! Wie das geht? - Ganz einfach, wenn man metallische oder Elektrische Gegenstände zu nah an der iPhone hält wird der Kompass gestört!

Und das kann man über die API des iPhone SDK abfragen. Also Hut ab für die Idee, ich habe dieses App noch nicht getestet. Vielleicht aber bald!

7Jul/091

Mangelhafte Software auf Sparkassenautomaten

Manchmal (bzw. fast jedes Mal wenn ich an einem Bankautomaten stehe) frage ich mich, welcher unterbezahlte Programmierer (wenn man ihn so schimpfen darf) eigentlich die Software für die Sparkassen Bankautomaten schreibt?

sparkasse_web

Warum ich mich hier so aufreg? Ok, dann will ich mal den Prozess beschreiben, durch den ich heute Morgen durch musste.

Also man kennt das ja, man steckt die Karte in den Leser, wartet kurz, tippt auf den Knopf wo "Abheben" steht und tippt seine PIN ein. So weit so gut, denn der interessante Teil kommt jetzt!

Ich wollte 15 Euro abheben, und habe dem entsprechend auf "einen anderen Betrag" gedrückt und "15" eingetippt. Nach einer kurzen Denkpause hat der Automat dann die Fehlermeldung "Betrag kann aus technischen Gründen nicht ausgezahlt werden" ausgespuckt.

Was soviel heißt wie "Es fehlen, Geldscheine, ohne die die gewünschte Summe nicht zusammengesetzt werden kann". Es fehlen also Scheine, aber natürlich sagt der Automat das nicht, da muss man dann irgendwie selbst drauf kommen.

Gut, dachte ich mir, tippst du hat einen neuen Betrag ein. Aber Fehlanzeige, denn statt den Benutzer zu einer erneuten Eingabe eines anderen Betrags aufzufordern, wird die Geldkarte wieder rausgespuckt und man muss wieder von Null anfangen und wieder die PIN eingeben etc.

Da ich das Geld heute Morgen aber brauchte, habe ich dann einen zweiten Anlauf gestartet und diesmal als Betrag "10" Euro eingegeben, und tada: wieder der selbe Fehler! Also hatte der Automat keine 5er und auch keine 10er Scheine mehr.

Aber trotzdem zeigt der Automat bei der Betragsauswahl alle Beträge an: 25, 50, 100 ... obwohl die meißten davon  nicht ausgezahlt werden können! - Warum? Warum wird keine Prüfung gemacht und die nicht auszahlbaren Beträge ausgeblendet? Warum kann man wenn der Betrag nicht verfügbar ist eine neue Eingabe tätigen?

Ich glaube das System auf diesen Automaten hat sich schon seit Jahren nicht mehr verändert, die Automaten werden neuer, bekommen Touchscreens aber denkt wer daran die Software zu aktualisieren? - Natürlich nicht!

Weiter so Sparkasse!

6Jul/090

Windows Presentation Foundation wird immer beliebter

Wer mit dem Begriff WPF (Windows Presentation Foundation) nichts anfangen kann, dem sei gesagt, dass das die Zukunft von Windows ist! Genau ist WPF die Art, wie Fenster und Inhalte auf der Grafischen Oberfläche (sozusagen 7es Layer) dargestellt werden.

Bisher (schon seit der Geburt von den Fenstern) hat GDI bzw, GDI+ die Darstellung der übernommen, doch nun ist Zeit für etwas neues! Denn wenn man mal über die Schulter schaut, und auf die Mac-Welt blickt wird man schnell sehen, dass die "normalen" Windows Anwendungen nicht mit den "Stylishen" Mac Programmen, die hüpfen und rotieren nicht mithalten können!

Genau da setzt WPF an und fügt verschiedenste 3D-, Video-, Bewegungs- und Vektorfunktionen ein, also das was bisher gefehlt hat.

Seit 2005 gibt es WPF übrigens schon und seit dem ist es auch schon fester Bestandteil von dem Visual Studio. Doch wird es bisher leider kaum noch eingesetzt, was wohl auch an der eher bescheidenen Implementierung im Visual Studio seitens Microsoft liegt.

Dabei sind die Funktionen revolutionär, und alle zukünftigen Applikationen von Microsoft, wie zB. Visual Studio 2010 werden darauf basieren und uns hoffentlich ein besseres Bedienerlebnis liefern können.

Es gibt aber natürlich auch heute schon Firmen, die den Trend erkannt haben und ihre Software überarbeiten. Denn diesen Mehrwert darf man nicht unterschätzen, denn jeder der schon mal mit einer Warenwirtschaftssoftware gearbeitet hat weis was ich meine!

Hier habe ich ein Beilspiel wie Warenwirtschaft aussehen könnte. "Auf den ersten Blick vermutet man, das ist ein ganz anderes Betriebssystem, bis man die Windows Taskleiste sieht" so geschrieben auf der Seite!

lawson_1

Und es ist wahr, wenn ich schon ein so schönes, "sexy" Programm sehe habe ich natürlich auch viel mehr Vergnügen daran damit zu arbeiten!

Ich hoffe es wird bald mehr Firmen geben, die WPF einsetzen werden und uns somit viele schöne, bunte Programme bescheren!

Und auch aus den selben Gründen werde ich hier auch in Zukunft einige Tutorials und kleine Beispiele zum Thema WPF posten.

6Jul/090

Bing ist auf dem Vormarsch

Nun sind schon einige Wochen vergangen, seit dem Microsoft seine neue Such Entscheidungsmaschine vorgestellt hat. Seit dem wächst ihr Marktanteil immer weiter.

Viele haben gesagt, dass der Marktanteil wieder sinken wird, da nun ersteinmal alle die neue Suche testen, aber dem war nicht so so könnte Bing wirklich eine ernste Gefahr für Google & Yahoo darstellen!

Und sogar ich merke, dass Bing immer öfter benutzt wird, denn als ich neulich in die Statistiken meines Blogs schaute, habe ich die ersten Bing-Referrer ausmachen können.

bild-110Wie man sieht, benutzen die Leute, die auf meinen Blog zugreifen nur Google und Bing, komisch, dass Yahoo hier nicht auftaucht! - Aber das zeigt ja auch nur noch einmal, dass Bing auf dem Vormarsch ist!

Ich selbst bin zwar immernoch zu sehr auf Google fixiert, aber nutze schon auch mal hin und wieder Bing - denn und da muss man mal ehrlich sein, so viel schlechter sind die Ergebisse von Bing nicht!

   
Get Adobe Flash playerPlugin by wpburn.com wordpress themes