Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.
Verlieren Sie auch hin und wieder den überblick über Ihr Dateisystem und finden partout bestimmte Dateien nicht mehr auf Dann ist neben der Windows-Suche ein Tool wie Everything ein guter Freund. Doch nicht nur die Anwendung mit ihrer Oberfläche selbst ist ein ausgereifter Helfer. Denn als Pluspunkt können Sie zusätzlich eine API-Schnittstelle verbuchen, welche sich aus Access heraus ansprechen lässt.
Everything
Gerade als Entwickler nennen Sie wahrscheinlich einen Rechner ihr Eigen, welcher mit Programmen, Tools, Dokumenten, Quellcode und anderen Dateien vollgestopft ist. Ich verzeichne aktuell über zwei Millionen Dateien auf meinem Hauptrechner. Da heißt es Ordnung halten und das Dateisystem gut strukturieren. Dennoch kommt es häufig vor, dass der Verbleib mancher Dateien schleierhaft bleibt – vor allem dann, wenn sie mehrfach in verschiedenen Verzeichnissen abgelegt wurden. Nun können Sie zum Auffinden selbstverständlich die Suche benutzen, die Windows von Haus aus mitbringt und, etwa unter Windows 10, die Suche namens Cortona befragen. Windows Search indiziert im Hintergrund fortwährend das System und füllt damit eine Datenbank, die im Ergebnis eine relativ schnelle Suche ermöglicht. Allerdings kann ich mich weder mit den möglichen Suchparametern, noch mit der Präsentation der Ergebnisse so richtig anfreunden. Deshalb gibt es eine Menge Tools, die die Lücke schließen und eine Alternative anbieten.
Everything von voidtools.com sticht dabei unter mehreren Aspekten heraus. Zwar gibt es mittlerweile diverse Freeware-Tools, die ebenso schnell arbeiten, weil sie als Basis die MFTs der Festplatten heranziehen, doch gerade die ausgefeilte API-Schnittstelle von Everything stellt ein Alleinstellungsmerkmal dar. Zudem haben Sie bei Installation die Wahl zwischen einer Systeminstallation und der Portable Version, bei der Sie den Download nur in ein Verzeichnis Ihrer Wahl entpacken.
Sollten Sie Everything noch nicht kennen, so lohnt sich zunächst ein Blick in die FAQ auf den Seiten von void-tools. Unter dem Menüpunkt Support finden Sie dort dann eine ausführliche Beschreibung aller Möglichkeiten und alles zum Umgang mit der Anwendung. Möchten Sie das Programm nutzen, so begeben Sie sich zu den Downloads und wählen unter verschiedenen Optionen die passende Version.
Es spielt, soviel vorweggenommen, keine Rolle, ob Sie die 32-Bit– oder die 64-Bit-Version nehmen. Normalerweise müssen Sie bei Office, das Sie sicherlich als 32-Bit-Version vorliegen haben, darauf achten, dass ein Programm, welches über eine API-Schnittstelle angesprochen wird, ebenfalls in 32 Bit vorliegt. Doch mit Everything wird gar nicht direkt kommuniziert, sondern über eine intermediäre DLL, die einen standardisierten Transportmechanismus von Windows nutzt, nämlich IPC. Der kann sich zwischen 32-Bit- und 64-Bit-Programmen gleichermaßen austauschen. Vielleicht liegt Ihnen die portable Version eher, als die Installation über eine Setup– oder MSI-Datei. Allerdings installiert letztere zunächst nicht einen Windows-Service, der das System automatisch im Hintergrund indiziert. Sie müssen dann die Anwendung erst unter Administratorrechten starten, was den bekannten UAC-Prompt unumgänglich macht. Erst dann scannt das Tool das System.
Wie funktioniert Everything
Wollten Sie das Dateisystem mit Bordmitteln von Windows auslesen und sich dabei solcher API-Funktionen, wie FindFileFirst, et cetera, bedienen, oder unter VBA die Dir-Funktion nutzen, oder einen Verweis auf ein File System Object setzen, um mit jenem zu arbeiten, so können Sie auch auf einem schnellen Rechner bei Millionen von Dateien eine lange Kaffeepause einlegen. Solche Verfahren sind zum Scannen des gesamten Dateisystems schlicht ungeeignet.
Everything nimmt sich jedoch direkt die MFT (Master File Table) einer Festplattenpartition zur Brust, die ein Verzeichnis aller Dateien und Ordner darstellt. Die Indizierung läuft deshalb unglaublich schnell ab. Bei den erwähnten zwei Millionen Dateien ist das hier in etwa 30 Sekunden erledigt. Ist die interne Everything-Datenbank einmal angelegt, so berücksichtigt das Tool nur noch änderungen am Dateisystem.
Nach dem Start dauert dies dann gerade einmal zwei bis fünf Sekunden, je nachdem, wieviel sich seit dem letzten Indizieren am Dateisystem geändert hat. Voraussetzung allerdings ist, dass das Dateisystem mit NTFS partitioniert ist. FAT kann Everything natürlich ebenfalls lesen, doch dann kommen die altbekannten Windows-API-Verfahren zum Zug, die erheblich langsamer sind.
Sinnvoll ist es deshalb, eine Verknüpfung zum Tool in den Autostart-Ordner von Windows zu legen, wenn Sie die portable Version verwenden. Nach dem Start der Anwendung minimiert sich diese in den Tray von Windows. Mit Doppelklick auf das entsprechende Lupen-Icon öffnet sich dann die Oberfläche des Tools. Ist Everything als Service installiert, so ist das Tray-Icon automatisch immer aktiv. Solange die Anwendung läuft, bekommt sie alle änderungen am Dateisystem mit und modifiziert demgemäß ihre Datenbank. Das lässt sich sogar Live beobachten, wenn Sie die Oberfläche von Everything geöffnet haben.
Das GUI von Everything zeigt Bild 1. Im Textfeld oben geben Sie einen Suchbegriff ein, und sofort filtert sich die Ergebnisliste unten entsprechend. Die Syntax für den Suchbegriff ist dabei weitaus mächtiger, als die von Windows. Hier können spezielle Platzhalter zum Einsatz kommen und allerlei Kombinationen derselben bis hin zu regulären Ausdrücken. Das Kombinationsfeld rechts neben dem Suchfeld erlaubt die einfache Filterung nach Kategorien, wie etwa Dokumente, Bilder oder Videos. Die Einträge in dieser Combobox können Sie zuvor selbst festgelegt haben.
Bild 1: Das GUI von Everything wirkt auf den ersten Blick recht unspektakulär und eher spartanisch, hat unter der Haube aber viel zu bieten.
Die Ergebnisliste ist nicht einfach nur eine Liste, sondern hat alle Eigenschaften der Windows-Shell. Das bedeutet, dass die gelisteten Dateien etwa dasselbe Kontextmenü anzeigen, wie der Explorer, und Kopier- sowie Drag And Drop-Operationen zulassen. Doch Gegenstand dieses Beitrags ist nicht die Beschreibung des Tools selbst, sondern seine Fernsteuerung. Zum Umgang mit der Anwendung finden Sie alle Informationen unter dem Menüpunkt Support auf voidtools. Sie werden staunen, was alles mit Everything möglich ist!
API-Schnittstelle
Unter dem Stichwort SDK können Sie auf voidtools die interaktive Schnittstelle zu Everything herunterladen. Die Funktionen der Schnittstelle sind zudem auf den Webseiten genau dokumentiert. über diese Schnittstelle können Sie dann dieselben Vorgänge realisieren, wie auch manuell über die GUI: Sie setzen einen Suchbegriff ab, übergeben einige Suchoptionen, und erhalten als Resultat eine Liste mit den gefundenen Dateien oder Ordner. Das GUI von Everything muss während dieses Vorgangs nicht geöffnet sein.
Nun hatte David Carpenter, der Entwickler von voidtools, mit diesem SDK nicht unbedingt Visual Basic-Entwickler als Zielgruppe im Sinn. Die Dokumentation stützt sich deshalb auf C. Zwar finden Sie im Netz – genauer: im Forum zu Everything – auch eine VB-Portierung der C-Header-Dateien, doch die ist etwas veraltet und hat nur eine Teilmenge der Methoden übersetzt.
Also hat Access im Unternehmen die Fleißarbeit für Sie übernommen und praktisch alle API-Aufrufe nach VBA übersetzt, sodass Sie nun auch aus einer Datenbank heraus Zugriff auf Everything haben – mit den gleichen Möglichkeiten, wie ein C++-Programmierer.
Alles, was Sie dazu benötigen, ist eine aktuelle Version der everything32.dll, die als Bestandteil des SDK den IPC-Mittler zwischen API und Everything-Anwendung oder -Dienst darstellt. Sie finden die DLL direkt im Download zur Demodatenbank. Und schließlich brauchen Sie noch alle API-Deklarationen zur DLL, die sich im Klassenmodul clsEverything der Demo befinden. Die kleine Testprozedur in Listing 1 zeigt zunächst, wie Sie dieses Modul einsetzen. Hier soll Everything untersuchen, wo sich alle Dateien mit dem Namen mscomctl.ocx befinden. Die Prozedur rufen Sie einfach aus dem VBA-Direktfenster heraus auf.
Sub TestEverything(Optional ByVal Search As String = "mscomctl.ocx") Dim C As New clsEverything Dim ret() As Variant Dim sCols As String Dim i As Long, j As Long ShellExecute 0&, "open", CurrentProject.Path & _ "\starteverything.vbs", vbNullString, CurrentProject.Path, 0& With C If .Ready Then Debug.Print .Version .CaseSensitive = False .ResultLimit = 2000 .ResultProperties = EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME Or _ EVERYTHING_REQUEST_DATE_CREATED .ShowMessages = True .SortOrder = EVERYTHING_SORT_PATH_ASCENDING If .Search(Search, ret, sCols) Then Debug.Print Replace(sCols, ";", vbTab) For i = 0 To UBound(ret, 2) For j = 0 To UBound(ret, 1) Debug.Print ret(j, i), Next j Debug.Print Next i End If End If End With End Sub
Listing 1: Die Verwendung des Klassenmoduls clsEverything demonstriert diese Test-Routine
Damit die Klasse funktioniert muss eine Instanz von Everything bereits auf dem Rechner laufen. Haben Sie es als Service installiert oder eine Verknüpfung zur everything.exe in den Autostart-Ordner von Windows gelegt, so ist dies bereits gewährleistet. Dann können Sie die erste Zeile der Prozedur löschen oder auskommentieren.
Die nämlich startet die im Datenbankverzeichnis befindliche VBScript-Datei starteverything.vbs über die API-Anweisung ShellExecute. Vielleicht kommt Ihnen das seltsam vor, weist doch VBScript keinerlei Methoden auf, die VBA selbst nicht auch enthielte. Man könnte also die Zeilen des Skripts ja auch in ein VBA-Modul schreiben
Tatsächlich gibt es damit ein Problem: Everything soll mit erhöhten Administratorrechten laufen. Ein Start aus Access heraus würde dies nicht gewährleisten können, da Access selbst nur unter Benutzerrechten läuft. Diesem Umstand hilft das Skript ab. Aus Platzgründen listen wir es hier nicht auf, erläutern jedoch dessen Funktionsweise. Der Trick ist nämlich, dass das Skript automatisch zweimal startet. Es ruft sich selbst auf, nachdem es über das WMI, je nach Betriebssystem, erfahren hat, wo sich die für die Skript-Ausführung verantwortliche Datei wscript.exe befindet.
Und die wird dann ein zweites Mal ausgeführt, wobei nun als Argument ein runas übergeben wird. Das weist den Kommandozeileninterpreter an, das Skript mit erhöhten Rechten zu startet. Es kommt nun zum UAC-Prompt. In der Folge ermittelt die Routine den Ort zur everything.exe und startet eine Instanz derselben unter Zuhilfenahme des Windows Scripting Host und der Methode Run. Everything ist nun automatisch mit Admin-Rechten gestartet. Das Skript findet dabei selbständig heraus, ob die 32-Bit– oder die 64-Bit-Version von Everything installiert ist. Nach diesem Start kann die Klasse clsEverything in Gestalt der Objektvariablen C, auf die ein With-Block gesetzt wird, in Aktion treten.
Mit der Eigenschaft Ready ermittelt die Prozedur erst, ob Everything als Prozess auch wirklich korrekt ansprechbar ist. Dann werden einige Suchoptionen eingestellt. CaseSensitive steht auf False, wodurch nicht zwischen Groß- und Kleinschreibung unterschieden wird. Die maximale Anzahl an Rückgabewerten (ResultLimit) wird auf 2.000 festgelegt. Das ist für diesen Testaufruf eigentlich nicht notwendig, da Sie wohl kaum mehr Dateien mit dem Namen mscomctl.ocx finden werden. (Bei mir waren es aber immerhin 94.)
übergeben Sie jedoch im Parameter Search der Prozedur etwa *.jpg, so ist die Wahrscheinlichkeit groß, dass dieses Limit überschritten wird. Dann stehen im Ergebnis nur die ersten 2.000 gefunden JPEG-Dateien.
Die nächste Einstellung ist da schon wichtiger. ResultProperties können Sie eine Kombination der im Klassenmodul definierten Enumerationskonstanten eEVERYTHING_REQUEST übergeben. Die steuern, was alles im Ergebnis enthalten sein soll. Die Standardeinstellung ist FULL_PATH_AND_FILE_NAME, die alle Dateien oder Ordner mit vollem Pfad zurückgibt. Weitere Angaben fehlen. Mit der zusätzlichen Einstellung DATE_CREATED weist das Ergebnis dann eine zusätzliche Spalte mit dem Erstelldatum der Dateien auf.
ShowMessages weist an, dass etwaige Fehlermeldungen ausgegeben werden sollen. Die Sortierung des Ergebnisses geschieht aufsteigend nach Namen, weil dies in SortOrder so eingestellt ist. Statt PATH_ASCENDING können Sie eine Menge anderer Sortierungen angeben. Mit SIZE_DESCENDING etwa würden die Ergebnisse absteigend nach Größe der Dateien sortiert.
Nach dieser (optionalen) Einstellung der Sucheigenschaften geht es mit dem Aufruf der Methode Search erst wirklich zur Sache. Dieser übergeben Sie im ersten Parameter den Suchausdruck, im zweiten ein Variant-Array (ret) und im dritten eine String-Variable sCols, welche die ermittelten Spalten im Ergebnis wiederspiegeln wird. Das Klassenmodul füllt diesen String selbst und gibt in unserem Beispiel über Debug.Print dies aus:
Kind; Path; File; DateCreated
Die erste Ergebnisspalte enthält demnach die Eigenschaft Kind der Dateien. Das ist ein Dateiattribut, welches aus einem Buchstaben besteht. Mit F ist eine Datei gekennzeichnet (File), mit D ein Ordner (Directory) und mit V ein Laufwerk (Volume). Path verweist auf den Dateipfad, File auf den Dateinamen. DateCreated gibt in der letzten Spalte das Erstelldatum aus. Nützlich ist dieser String dann, wenn Sie die Elemente des Rückgabe-Arrays etwa den Spalten einer Tabelle zuordnen möchten, wie wir noch sehen werden.
Das in ret zurückgegebene Array ist damit zweidimensional. Die zweite mit UBound(ret, 2) ermittelte Dimension ist die Anzahl der Zeilen, die erste die Zahl der Spalten darin. Zwei verschachtelte Schleifen mit den Zählern i und j auf diese Dimensionen durchlaufen nun das Array und geben die Werte im Direktfenster aus. Das abschließende Komma in Debug.Print weist VBA an, den nächsten Eintrag jeweils an die nächste TAB-Position zu stellen. Nach der inneren Schleife führt das Debug.Print ohne Parameter hingegen zum Zeilenumbruch.
Im Test dauerte der Aufruf von Search etwa ein bis zwei Sekunden. Vergegenwärtigen Sie sich, dass dabei das komplette Dateisystem durchsucht wird! Am schnellsten geschieht das, wenn Sie lediglich die Dateipfade zurückgeben lassen. Mit jedem weiteren Parameter steigt die Rechenzeit an. Besonders kritisch ist das beim Einsatz der Suchoption SIZE, die auch die Größe der Dateien ermittelt. Das übernimmt im Klassenmodul nämlich nicht Everything selbst, sondern eine Hilfsfunktion. Würden Sie als Suchausdruck etwa *.* angeben, so müsste für alle zwei Millionen im System gefundenen Dateien deren Größe ermittelt werden – vorausgesetzt, Sie hätten für ResultLimit einen entsprechenden Wert angegeben.
Abgesehen davon, dass ein Array mit 2 Millionen Werten wohl den Speicher von VBA sprengen würde, dürfte die Ermittlung dieser Dateigrößen locker eine Stunde benötigen. Ausprobiert haben wir das nicht.
ResultLimit ist übrigens auf 100.000 voreingestellt, wenn Sie die Eigenschaft nicht selbst setzen. Trotz der Performance von Everything kann die Ermittlung des Ergebnisses, je nach Situation, etwas dauern, weshalb es sich im Zweifel gut macht, vor dem Aufruf von Search ein DoCmd.Hourglass True abzusetzen, um die Sanduhr erscheinen zu lassen.
Klassenmodul clsEverything
Die Hauptaufgabe des Moduls ist die übersetzung der C-Header von Everything nach VBA. Im Kopf finden Sie deshalb eine Menge von Konstantendefinitionen und API-Deklarationen. Die werden dann von den Prozeduren des Moduls verwendet. Außerhalb des Moduls brauchen Sie sich keine Gedanken über diese API-Deklarationen zu machen, die ja auch alle als Private daherkommen.
Im Initialize-Ereignis der Klasse (siehe Listing 2) werden einige Voreinstellungen für die Suche vorgenommen. Das Ergebnis-Limit steht auf 100.000, die Rückgabespalten bestehen nur aus Pfad und Name der Datei, die Sortierung geschieht nach Namen, und zwischen Groß- und Kleinschreibung soll nicht unterschieden werden. Diese Einstellungen speichern die Member-Variablen des Moduls, welche namentlich jeweils mit m_ beginnen.
Private Sub Class_Initialize() m_sort = EVERYTHING_SORT_PATH_ASCENDING m_requestprops = EVERYTHING_REQUEST_FILE_NAME Or _ EVERYTHING_REQUEST_PATH m_msg = True m_limit = 100000 m_case = False hMod = GetModuleHandle("everything32") If hMod = 0 Then If Len(Dir(CurrentProject.Path & "\everything32.dll")) = 0 Then MsgBox _ "Die Datei everything32.dll muss im Datenbankverzeichnis liegen!", _ vbCritical, "Everything" Else hMod = LoadLibrary(CurrentProject.Path & "\everything32.dll") End If End If End Sub
Listing 2: Im Initialize-Ereignis werden Suchparameter voreingestellt und die everything-DLL geladen
Damit die API-Deklarationen greifen, muss die für die Kommunikation mit Everything verantwortliche Datei everything32.dll geladen werden. Das muss nur einmal geschehen. GetModuleHandle fragt ab, ob das Modul bereits im Prozessraum von Access steht. Dann ist alles ok und hMod ist ungleich Null. Ansonsten schaut die Routine erst einmal nach, ob die everything32.dll überhaupt im Datenbankverzeichnis liegt (CurrentProject.Path), was die Voraussetzung für die Klasse ist. Sie können diesen Zweig natürlich modifizieren und die DLL etwa in einem anderen Verzeichnis unterbringen. Ist die Datei vorhanden, so lädt LoadLibrary sie und speichert das erhaltene Modul-Handle in hMod.
Selbst dann, wenn die DLL geladen ist, muss zusätzlich überprüft werden, ob das Anwendungsfenster von Everything schon aufgebaut ist. über dieses Fenster geschieht nämlich die Kommunikation in Form von IPC-Messages. Sichtbar muss es dazu allerdings nicht sein. Es reicht, wenn Everything als Icon im Tray zu finden ist. Die Eigenschaft Ready des Klassenmoduls sucht nun erst nach dem Fenster und anschließend nach dem Zustand von Everything, welcher über die API-Funktion Everything_IsDBLoaded abgefragt werden kann. Starten Sie Everything etwa über das besprochene VBS-Script, so beginnt Everything zunächst mit der Indizierung der modifizierten Dateien des Systems, um diese änderungen in seiner Datenbank abzulegen. Das kann einige Zeit in Anspruch nehmen und erst nach Ablauf dieser gibt Everything_IsDBLoaded einen anderen Wert aus, als Null:
If Everything_IsDBLoaded Then Ready = True Else MsgBox "Die Everything-Datenbank ist noch nicht" & _ "vollständig geladen. Bitte später nochmals " & _ "versuchen.", vbCritical, "Everything" End If
Die ganzen Property-Prozeduren des Moduls übernehmen im Wesentlichen die Aufgabe, die übergebenen Parameter in Member-Variablen abzuspeichern, die dann erst beim Aufruf von Search ausgewertet werden. Und diese Prozedur ist das Kernstück des Moduls. Deklariert ist sie so:
Function Search( _ ByVal QueryString As String, _ ResultSet() As Variant, _ ByRef Columns As String) As Boolean
In QueryString übergeben Sie den Suchausdruck, in ResultSet das undimensionierte Rückgabe-Array vom Typ Variant, und in Columns eine String-Variable, die anschließend die Spalten des Ergebnisses enthält. Gültig ist das Ergebnis indessen nur dann, wenn die Funktion True zurückgibt. Haben Sie etwa ShowMessages auf False eingestellt, dann unterbleiben Fehlermeldungen. In diesem Fall gibt nur der Rückgabewert der Funktion Aufschluss über den Erfolg der Suche.
In der Testprozedur von Listing 1 wird dieser Rückgabewert auch ausgewertet.
Search stellt nun anhand der über die Eigenschaft ResultProperties eingestellten Suchoptionen über die Member-Variable m_requestprops die Spalten des Ergebnisses zusammen:
Columns = "Kind;Path;File;" If (m_requestprops And REQUEST_EXTENSION) Then bExt = True: Columns = Columns & "Extension;" If (m_requestprops And REQUEST_DATE_CREATED) Then bDateCr = True: Columns = Columns & "DateCreated;" If (m_requestprops And REQUEST_DATE_MODIFIED) Then bDateMod = True: Columns = Columns & "DateModified;" ...
Die Boolean-Variablen bExt, bDateCr, bDateMod et cetera steuern den weiteren Ablauf in der Prozedur. Der Suchausdruck wird nun an Everything übergeben:
Everything_SetSearchW (StrPtr(QueryString)) ret = Everything_QueryW(1)
QueryW ermittelt hier, ob der übergebene Suchausdruck korrekt ist und den Spezifikationen von Everything genügt. Nur dann steht der Rückgabewert in ret auf 1. Tatsächlich führt dieser Aufruf bereits zur Suche und das Ergebnis kann anschließend ausgewertet werden:
cnt = Everything_GetNumResults
In cnt steht jetzt die Zahl der ermittelten Dateien oder Ordner. Ist sie gleich 0, so wird die Prozedur verlassen. Andernfalls fährt Sie mit der überführung der Ergebnisse in das Array ResultSet fort:
If cnt > 0 Then Search = True: ReDim ResultSet(n - 1, cnt - 1) For i = 0 To cnt - 1 (...) Next i End If
Das Array wird anhand der Ergebniszahl cnt dimensioniert. Das gilt für die zweite Dimension. Die erste bestimmt die Zahl der Spalten, die vorher in n bei der Zusammenstellung derselben ermittelt wurde.
Den Dateinamen erhält die Routine nun etwa so:
sFile = String(260, 0) ret = Everything_GetResultFullPathNameW( _ i, StrPtr(sFile), 260&) If ret > 0 Then sFile = Left(sFile, ret)
Die String-Variable sFile muss zuerst mit Null-Characters gefüllt werden, wie dies häufig bei API-Funktionen erforderlich ist. Der String wird nicht direkt übergeben, sondern als Zeiger per StrPtr. In ret steht dann die Buchstabenzahl, die die API-Funktion errechnete. Left schneidet die nun überflüssigen Null-Characters ab.
übrigens fährt die Prozedur mit den weiteren Schritten grundsätzlich nur dann fort, wenn ret hier auf >0 steht. Denn der Datei- oder Ordnername sind das Mindeste, was im Ergebnis auftauchen muss.
Soll auch das Erstelldatum abgefragt werden, so tritt dieser Zweig in Aktion:
If bDateCr Then ret = Everything_GetResultDateCreated(i, FilTime) If ret <> 0 Then ResultSet(col, i) = APITimeToDate(FilTime) End If col = col + 1 End If
Die API-Funktion füllt eine Variable FillTime des benutzerdefinierten Typs FILETIME, der erst mit der Hilfsfunktion APITimeToDate im Modul in ein VBA-Datum überführt wird. Der Spaltenzähler col erhöht sich nun.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
Testzugang
eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel
diesen und alle anderen Artikel mit dem Jahresabo