15. Aug 2022
Lesedauer 8 Min.
Chrome-Lesezeichen anbinden
Ein eigenes Steuerelement für Webinhalte, Teil 12
Nachdem die Objektstruktur für Google-Chrome-Lesezeichen angelegt ist, erweitern Sie die Favoriten des Systems.

Der wechselseitige Gebrauch unterschiedlicher Webbrowser gestaltet sich hinsichtlich der darüber verwalteten Favoriten beziehungsweise Lesezeichen als äußerst schwierig. Der Grund: Es gibt kein einheitliches Lesezeichen-Format für alle Webbrowser, und auch wechselseitige Export- und Importfunktionen werden nicht bereitgestellt. Dementsprechend müssen Sie sich in eigenen Browser-Anwendungen selbst darum kümmern.In dieser Serie wurde bereits gezeigt, wie Sie ein benutzerdefiniertes Webbrowser-Steuerelement definieren sowie die Favoriten des Systems und des Internet Explorers auslesen, erweitern und über ein benutzerdefiniertes Favoritensteuerelement strukturiert ausgeben (siehe [1] bis [8]). Für die Unterstützung der Chrome-Lesezeichen wurde in [9] bis [11] gezeigt, wie Sie die im JSON-Format verwalteten Lesezeichen in eine Objektstruktur überführen. Diese Objektstruktur vom Typ ChromeBookmarks vereinfacht die Übernahme der Lesezeichen in das Favoritenverzeichnis des Windows-Systems, das auch vom Internet Explorer verwendet wurde.Die Objektstruktur ist in Bild 1 dargestellt. Alle Lesezeichen werden in drei Hauptebenen (roots) verwaltet, wobei jede Hauptebene (root) ihrerseits Lesezeichen (childrens) und untergeordnete Ordnerauflistungen (FolderDatas) enthalten kann. Jeder untergeordnete Ordner (FolderData) besitzt neben Lesezeichen (childrens) wiederum untergeordnete Ordner (SubFolderDatas), die sich erneut aus Lesezeichen und Unterordnern zusammensetzen.

Ausgangspunktfür die Übernahme der Chrome-Lesezeichen ist die benutzerdefinierte Datenstruktur ChromeBookmarks(Bild 1)
Autor
Das Ganze ist in variablen Ebenen möglich. Zu jedem Ordner wird der Name (name), das Anlagedatum (date_added) sowie das letzte Änderungsdatum (date_modified) gespeichert. Lesezeichen werden über Children-Objekte verwaltet, die das Datum des Hinzufügens (date_added), den Namen (name) und insbesondere den URL (url) enthalten.
Chrome-Lesezeichen in Favoriten umwandeln
Die gerade beschriebene Objekthierarchie ist Grundlage für die Übernahme der Chrome-Lesezeichen in das Favoritenverzeichnis. Dazu wird die Objektstruktur in eine Verzeichnisstruktur überführt, die ihrerseits als Unterordner im Favoritenverzeichnis eingerichtet wird. Das Hauptunterverzeichnis entspricht dem Haupteintrag ChromeBookmarks, in dem drei Unterverzeichnisse mit den Hauptknoten (roots) eingerichtet werden (bookmark_bar, synced und other). In den einzelnen Hauptordnern können ein oder mehrere Lesezeichen über entsprechende URL-Dateien angelegt und verwaltet werden. In jedem Ordner sind ein oder mehrere Unterordner enthalten, die ihrerseits URL-Definitionen und weitere Unterordner (mit geschachtelten Ebenen) enthalten können. Die Struktur entspricht der Objekthierarchie ChromeBookmarks (Bild 2).
Die Verzeichnisstrukturzur Objekthierarchie ChromeBookmarks(Bild 2)
Autor
Von der Objekthierarchie zur Verzeichnisstruktur
Alle Importfunktionen für die Chrome-Lesezeichen werden direkt in das Anzeigesteuerelement FavoritesCtl übernommen, das die Favoritenverwaltung in der Benutzeroberfläche vornimmt. Die Importfunktionen finden Sie in der Klasse BrowserFunctions, welche über die nachfolgende Anweisung global in das Benutzersteuerelement eingebunden und instanziert wird:
<span class="hljs-keyword">Dim </span><span class="hljs-keyword">bfObj </span>As New <span class="hljs-keyword">BrowserFunctions </span>
Darauf aufbauend realisieren Sie die Importfunktion ImportChromeBookmarks. Darin lesen Sie zunächst die aktuellen Chrome-Bookmarks in die Verzeichnisstruktur ein. Die Objektstruktur zu den Lesezeichen ermitteln Sie über die Methode bfObj.GetBookmarks und übernehmen diese in die Objektvariable cb. Im Anschluss daran übernehmen Sie die eingelesenen Lesezeichen in den Favoritenordner des Systems, indem Sie per For-Each-Schleife alle Hauptordner der roots-Auflistung durchlaufen. Jeder verarbeitete Hauptordner wird dabei an die Methode ImportRoot übergeben, die jeweils nur diesen Hauptordner importiert. Ist das Importieren abgeschlossen, wird die Anzeige mit allen Favoriten über die Methode ShowFavorites neu aufgebaut.
<span class="hljs-keyword">Sub</span> ImportChromeBookmarks()
<span class="hljs-keyword">Dim</span> cb <span class="hljs-keyword">As</span> ChromeBookmarks =
bfObj.GetBookmarks()
<span class="hljs-keyword">With</span> cb
<span class="hljs-keyword">For</span> <span class="hljs-keyword">Each</span> MainFolder <span class="hljs-keyword">As</span> Root <span class="hljs-keyword">In</span> .roots
ImportRoot(MainFolder)
<span class="hljs-keyword">Next</span>
<span class="hljs-keyword">End</span> <span class="hljs-keyword">With</span>
ShowFavorites()
<span class="hljs-keyword">End</span> <span class="hljs-keyword">Sub</span>
Die Methode ImportRoot(Listing 1) verarbeitet einen Hauptordner des Typs Root, der per Parameter übergeben wird. Per Definition sind zunächst drei Hauptordner vorgegeben. Im ersten Schritt wird zunächst das Zielverzeichnis für die URL-Dateien bestimmt. Um die Inhalte abzuheben, wird ein eigener Unterordner als Basis eingerichtet, über den alle Importe später komplett wieder zu entfernen, zu sichern und gegebenenfalls zu verschieben sind. Dieser Unterordner trägt den Namen Google Chrome und befindet sich im Favoritenordner, der mit bfObj.GetFavoritesFolder abgefragt wird.
Listing 1: Hauptordner der Chrome-Lesezeichen importieren
Sub ImportRoot(ByVal MainFolder <span class="hljs-keyword">As</span> Root) <br/><br/> <span class="hljs-keyword">Dim</span> MainFavoritesFolder <span class="hljs-keyword">As</span> String = <br/> bfObj.GetFavoritesFolder() &amp; <br/> <span class="hljs-string">"\Google Chrome"</span> <br/> <span class="hljs-keyword">Dim</span> RootFolderName <span class="hljs-keyword">As</span> String = <span class="hljs-string">""</span> <br/> <span class="hljs-keyword">Select</span> <span class="hljs-keyword">Case</span> MainFolder.Name <br/> <span class="hljs-keyword">Case</span> <span class="hljs-string">"bookmark_bar"</span> <br/> RootFolderName = <span class="hljs-string">"Lesezeichen"</span> <br/> <span class="hljs-keyword">Case</span> <span class="hljs-string">"other"</span> <br/> RootFolderName = <span class="hljs-string">"Andere"</span> <br/> <span class="hljs-keyword">Case</span> <span class="hljs-string">"synced"</span> <br/> RootFolderName = <span class="hljs-string">"Synchronisiert"</span> <br/> <span class="hljs-keyword">Case</span> <span class="hljs-keyword">Else</span> <br/> 'für spätere Erweiterungen unverändert <br/> ' übernehmen <br/> RootFolderName = MainFolder.Name <br/> <span class="hljs-keyword">End</span> <span class="hljs-keyword">Select</span> <br/> <span class="hljs-keyword">Dim</span> RootFolder <span class="hljs-keyword">As</span> String = <br/> MainFavoritesFolder &amp; <span class="hljs-string">"\"</span> &amp; RootFolderName <br/> CreateFavoritesFolder(RootFolder) <br/> ImportFolderDatas( <br/> RootFolder, MainFolder.FolderDatas) <br/> ImportChildrens( <br/> RootFolder, MainFolder.Childrens) <br/><span class="hljs-keyword">End</span> Sub
Über eine Select-Case-Auswahl werden die von Chrome genutzten root-Bezeichner durch deutsche Namen ersetzt, die dann im Steuerelement angezeigt werden und die ihrerseits als Unterordner über die Methode CreateFavoritesFolder angelegt werden, sofern sie noch nicht vorhanden sind.Im Anschluss daran werden mit ImportFolderDatas die untergeordneten Ordner unter Angabe des jeweiligen Zielordners (RootFolder) und der zugehörigen FolderDatas-Auflistung eingelesen. Die im Hauptordner enthaltenen Lesezeichen werden abschließend mit der Methode ImportChildrens übernommen.Die Methode CreateFavoritesFolder übernimmt einen übergebenen Pfad (Folder) und legt diesen, sofern er noch nicht existiert, mit Directory.CreateDirectory an.
Sub CreateFavoritesFolder(ByVal Folder As String)
If Directory.Exists(Folder) = False Then
Directory.CreateDirectory(Folder)
End If
End Sub
Unterverzeichnisse der Hauptordner werden in Chrome zusammen mit dem Anlagedatum und dem Datum der letzten Änderung gespeichert. Und auch zu einzelnen URLs wird ein Anlagedatum abgelegt.
Darüber ist es später möglich, veraltete URLs und/oder Ordner herauszufiltern beziehungsweise zu löschen oder Ordner und/oder URLs nach Datum zu suchen. Die intern als Zeichenketten verwalteten Chrome-Datumswerte, hinter denen sich lange Ganzzahlwerte verbergen, sind allerdings nicht direkt nutzbar und müssen zunächst in entsprechende DateTime-Objekte konvertiert werden. Diese Aufgabe übernimmt die benutzerdefinierte Konvertierfunktion ChromeDate2DateTime. Dieser wird das Chrome-Datum als Zeichenkette übergeben. Die Funktion liefert das Ergebnis als DateTime-Objekt zurück (Listing 2). Die interne Umwandlung wird schrittweise im Quelltext dokumentiert.
Listing 2: Chrome-Datum konvertieren
Function ChromeDate2DateTime( <br/> ByVal ChromeDate As String) As DateTime <br/> ' String in Long umwandeln (Millisekunden) <br/> Dim mSeconds As Long = CLng(ChromeDate / 1000) <br/> ' Basis zur Zeitangabe (Epoche) <br/> Dim epoch As DateTime = New DateTime(1601, 1, 1) <br/> ' Umrechnung in Ticks (dasZehnmillionstel <br/> ' einer Sekunde) <br/> Dim epochValue As Long = epoch.Ticks <br/> ' Umwandeln in UTC-Zeit (Koordinierte Weltzeit) <br/> Dim utcTime As DateTime = <br/> New DateTime(epochValue + mSeconds * 10) <br/> ' Rückgabe in lokalisierter Zeit <br/> Return utcTime.ToLocalTime <br/>End Function
ImportFolderDatas erledigt den Import einzelner Datenordnerauflistungen. Der Methode übergeben Sie den Ausgangsordner (Folder) für die Verarbeitung als String sowie die zugehörige FolderDatas-Auflistung (fd), siehe Listing 3.
Listing 3: Datenordner importieren
Sub ImportFolderDatas(ByVal Folder As String, <br/> ByVal fd As FolderDatas) <br/><br/> For Each SubFolder As FolderData In fd <br/> Dim SubFolderName As String = <br/> Folder &amp; "\" &amp; SubFolder.name <br/> CreateFavoritesFolder(SubFolderName) <br/> Directory.SetCreationTime(SubFolderName, <br/> ChromeDate2DateTime(SubFolder.date_added)) <br/> Directory.SetLastWriteTime(SubFolderName, <br/> ChromeDate2DateTime(SubFolder.date_modified)) <br/> If SubFolder.SubFolderDatas IsNot Nothing Then <br/> ImportFolderDatas(SubFolderName, <br/> SubFolder.SubFolderDatas) <br/> End If <br/> ImportChildrens(SubFolderName, <br/> SubFolder.Childrens) <br/> Next <br/>End Sub
In der Methode werden alle FolderData-Elemente der übergebenen FolderDatas-Auflistung über eine For-Each-Schleife durchlaufen. Die Verarbeitung erfolgt dabei verzeichnisbezogen. Der Name des ersten Unterordners wird über die Variable SubFolderName bestimmt, die sich aus den Namen von Haupt- und Unterordner zusammensetzt. Mit CreateFavoritesFolder wird der neue Unterordner, sofern nicht vorhanden, angelegt. Ist der Ordner angelegt, werden das Anlagedatum und das Datum der letzten Änderung entsprechend dem Chrome-Element übernommen. Dazu kommen die Methoden Directory.SetCreationTime (Änderung Anlagedatum) und Directory.SetLastWriteTime (Änderung Datum der letzten Änderung). Die Umwandlung des Chrome-Datumswertes übernimmt dabei jeweils die Funktion ChromeDate2DateTime.Finden sich im aktiv verarbeiteten Unterordner (SubFolder) weitere SubFolderDatas-Auflistungen, so werden diese rekursiv verarbeitet. Abschließend wird mit ImportChildrens jeweils die zum aktuell verarbeiteten Unterordner gehörende Lesezeichenauflistung SubFolder.Childrens importiert.Die Methode ImportChildrens dient der Übernahme der in einem Ordner vorhandenen URLs. Der Zielordner wird über den Parameter DestinationFolder, die URL-Auflistung vom Typ Childrens über den Parameter Entries übergeben. Alle Einträge werden erneut per For-Each-Schleife verarbeitet. Mit der Methode CreateChromeBookmarkURL wird ein einzelner URL übernommen, der in einem Ordner (zum Beispiel root) oder beliebigen Unterordnern definiert ist.
Sub ImportChildrens(ByVal DestinationFolder As String,
ByVal Entries As Childrens)
For Each Entry As Children In Entries
CreateChromeBookmarkURL(DestinationFolder, Entry)
Next
End Sub
Das nächste Problem, das an dieser Stelle auftaucht, ist, dass die Namenseigenschaften, die in den Chrome-Lesezeichen verwendet werden, mitunter in Dateinamen unzulässig sind. Die benutzerdefinierte Funktion FilterUnsupportedtFileChars ersetzt oder entfernt diese Zeichen. Die Funktion ist dabei so aufgebaut, dass sie jedes einzelne unzulässige Zeichen durch ein alternatives, in Dateinamen zulässiges Zeichen ersetzt, siehe Listing 4.
Listing 4: Dateinamen säubern
Function FilterUnsupportedtFileChars( <br/> ByVal FileName As String) As String <br/><br/> Dim fn As String = FileName <br/> fn = fn.Replace("[", "(") <br/> fn = fn.Replace("]", ")") <br/> fn = fn.Replace("\", "") <br/> fn = fn.Replace("/", "") <br/> fn = fn.Replace("?", "") <br/> fn = fn.Replace("*", "") <br/> fn = fn.Replace("^", "") <br/> fn = fn.Replace(Chr(34), "") <br/> fn = fn.Replace("&lt;", "") <br/> fn = fn.Replace("&gt;", "") <br/> fn = fn.Replace("]", "") <br/> fn = fn.Replace("|", "") <br/> Return fn <br/>End Function
Einzelne URL-Dateien werden jeweils über die Methode CreateChromeBookmarkURL generiert. Dieser übergeben Sie den Zielordner im Favoritenverzeichnis (Folder) und ein URL-Eintragsobjekt vom Typ Children (c), siehe Listing 5.
Listing 5: URL-Datei zu einem Chrome-Lesezeichen anlegen
Sub CreateChromeBookmarkURL( <br/> ByVal Folder As String, ByVal c As Children) <br/><br/> Dim UrlFileWithPath As String = Folder &amp; "\" &amp; <br/> FilterUnsupportedtFileChars(c.name) &amp; ".url" <br/> Dim IconFile As String = <br/> GetIconPathForGoogleBookmark(c.url) <br/> Dim IconIndex As Integer = 1 <br/><br/> bfObj.CreateUrlShortcut(UrlFileWithPath, <br/> c.url, IconFile, IconIndex) <br/> File.SetCreationTime(UrlFileWithPath, <br/> ChromeDate2DateTime(c.date_added)) <br/>End Sub
In der Methode wird zunächst der URL-Dateiname samt Pfad in der Variablen UrlFileWithPath zusammengefasst, um dann das Standardsymbol zur Symboldatei über die Funktion GetIconPathForGoogleBookmark unter Angabe des zugehörigen URLs (c.url) zuzuordnen. Zur Dateianlage kommt die bereits definierte Methode bfObj.CreateUrlShortcut zum Einsatz. Abschließend wird das Anlagedatum der neu angelegten URL-Datei auf das URL-Anlagedatum des zugehörigen Chrome-Lesezeichens gesetzt. Hierbei kommen die Methoden File.SetCreationTime und ChromeDate2DateTime sowie die Eigenschaft c.date_added zum Einsatz.Standardsymbole zu Domänen können Sie über Google abrufen. GetDomainString ist eine Funktion, welche die zu einem URL gehörende Domäne abspaltet und als String zurückliefert. Dazu wird ein URI mit der URL-Adresse angelegt, um den zugehörigen Host über die gleichnamige Eigenschaft abzuspalten und zurückzugeben.
Function GetDomainString(ByVal UrlString As String)
Dim uri As Uri = New Uri(UrlString)
Return uri.Host
End Function
Die Suchadresse zu einem URL-spezifischen Bildsymbol fragen Sie über die Funktion GetIconPathForGoogleBookmark ab, der Sie die URL-Adresse übergeben. Die Funktion gibt die HTTP-Adresse zum Symbol unter Angabe des Domänennamens zurück. Das Ergebnis wird direkt in URL-Dateien übernommen.Chrome selbst weist Lesezeichen nicht direkt ein Bildsymbol zu. Ursprünglich wurde der Rumpf der Funktion GetIconPathForGoogleBookmark in der Klasse BrowserFunctions definiert, nun aber in die Steuerelementklasse FavoritesCtl verschoben.
Function GetIconPathForGoogleBookmark(
ByVal UrlString As String) As String
Return "http://www.google.com/s2/favicons?domain=" &
GetDomainString(UrlString)
End Function
Kontextmenü erweitern
Nachdem die Übernahmefunktionen codiert sind, können Sie diese über das Favoritensteuerelement und das zugehörige Symbolleistensteuerelement bereitstellen. Bild 3 zeigt die neuen Kontextmenüdefinitionen im Entwurfsmodus von Visual Studio.
Die neuenImport-und Verwaltungsfunktionen stellt das Steuerelement FavoritesCtl.vb über einerweitertes Kontextmenü bereit(Bild 3)
Autor
Tabelle 1 fasst die Kontextmenü-Methoden, die aufgerufenen Funktionen und deren Beschreibungen zusammen. Die einzelnen Funktionen sollen hier nicht mehr detailliert behandelt werden. Die meisten Funktionen werden zusätzlich über öffentliche Methoden für den externen Aufruf bereitgestellt. Details entnehmen Sie bitte dem Code im Download zum Artikel. So viel für diesmal. In der kommenden Folge dieser Serie geht es weiter mit dem Sichern, Wiederherstellen und Löschen von Favoriten.
Tabelle 1: Neue Kontextmenübefehle im Favoritensteuerelement
|
Fussnoten
- Andreas Maslo, Erweitertes Browser-Control, Ein eigenes Steuerelement für Webinhalte, Teil 1, dotnetpro 10/2021, Seite 136 ff., http://www.dotnetpro.de/A2110BasicInstinct
- Andreas Maslo, Code für den Browser, Ein eigenes Steuerelement für Webinhalte, Teil 2, dotnetpro 11/2021, Seite 136 ff., http://www.dotnetpro.de/A2111BasicInstinct
- Andreas Maslo, Den Browser ausbauen, Ein eigenes Steuerelement für Webinhalte, Teil 3, dotnetpro 12/2021, Seite 136 ff., http://www.dotnetpro.de/A2112BasicInstinct
- Andreas Maslo, Webseiten prüfen, Ein eigenes Steuerelement für Webinhalte, Teil 4, dotnetpro 1/2022, Seite 134 ff., http://www.dotnetpro.de/A2201BasicInstinct
- Andreas Maslo, Favoriten verwalten, Ein eigenes Steuerelement für Webinhalte, Teil 5, dotnetpro 2/2022, Seite 134 ff., http://www.dotnetpro.de/A2202BasicInstinct
- Andreas Maslo, Icons für die Favoriten, Ein eigenes Steuerelement für Webinhalte, Teil 6, dotnetpro 3/2022, Seite 134 ff., http://www.dotnetpro.de/A2203BasicInstinct
- Andreas Maslo, Favoriten unter Kontrolle, Ein eigenes Steuerelement für Webinhalte, Teil 7, dotnetpro 4/2022, Seite 134 ff., http://www.dotnetpro.de/A2204BasicInstinct
- Andreas Maslo, Sonderfunktionen, Ein eigenes Steuerelement für Webinhalte, Teil 8, dotnetpro 5/2022, Seite 134 ff., http://www.dotnetpro.de/A2205BasicInstinct
- Andreas Maslo, Endspurt, Ein eigenes Steuerelement für Webinhalte, Teil 9, dotnetpro 6/2022, Seite 134 ff., http://www.dotnetpro.de/A2206BasicInstinct
- Andreas Maslo, Google Chrome Bookmarks, Ein eigenes Steuerelement für Webinhalte, Teil 10, dotnetpro 7/2022, Seite 134 ff., http://www.dotnetpro.de/A2207BasicInstinct
- Andreas Maslo, Methoden für Bookmarks, Ein eigenes Steuerelement für Webinhalte, Teil 11, dotnetpro 8/2022, Seite 134 ff., http://www.dotnetpro.de/A2208BasicInstinct