Dateiverknüpfungen mit Visual Basic 6.0 erstellen und auslesen
Veröffentlicht: 12. Jun 2003 | Aktualisiert: 24. Jun 2004
Von Mathias Schiffer
Die Microsoft Newsgroups sind eine Quelle schier unerschöpflichen Wissens, das nahezu auf Knopfdruck abrufbar ist: Hunderte deutschsprachige Entwickler vom Einsteiger bis zum Profi treffen sich hier täglich virtuell, um Fragen zu stellen oder zu beantworten. Auch themennahe Probleme, Ansichten und Konzepte werden miteinander diskutiert. Sie kennen die Microsoft Newsgroups noch nicht? Detaillierte Information für Ihre Teilnahme finden Sie auf der Homepage der Microsoft Support Newsgroups.
Diese Kolumne greift regelmäßig ein besonders interessantes oder häufig nachgefragtes Thema aus einer der Entwickler-Newsgroups auf und arbeitet es aus.
Auf dieser Seite
Aus der Visual Basic Newsgroup microsoft.public.de.vb:
Aus der Visual Basic Newsgroup microsoft.public.de.vb:
Ich versuche, mit Visual Basic 6 eine Verknüpfung auf eine Datei zu erstellen. Das alles sollte ohne zusätzliche Dateien wie DLL's etc. funktionieren. Welche Möglichkeiten gibt es da?
Die Dateiverknüpfungen (im Englischen: "Shell Links") kennen Sie aus Ihrem Windows-Startmenü: Die meisten Anwendungen hinterlassen dort bei ihrer Installation eine Programmgruppe mit Verknüpfungen auf die Anwendungsdatei, mitunter auch auf Hilfematerialien und weitere Dateien, die andernorts auf der Festplatte abgelegt sind. Die Verknüpfungen im Startmenü selber sind Dateien, die in entsprechenden Unterverzeichnissen des Startmenüordners des Benutzers abgelegt wurden. In diesen Dateien werden die Eigenschaften der Verknüpfung - vom Anwendungspfad über das Icon der Verknüpfung bis hin zu einem Kommentar - abgespeichert.
Wie geht das Installationsprogramm SETUP1.VBP vor?
Da das mit Visual Basic selber mitgelieferte Installationsprogramm für Ihre Anwendungen erfreulicherweise im Sourcecode vorliegt (SETUP1.VBP) und dieses bei der Installation Ihrer Anwendungen Verknüpfungen im Startmenü erzeugen kann, lohnt sich offenbar ein Blick in den Sourcecode dieses Installationsprogramms.
Hier finden Sie im Modul basCommon die folgende Funktion deklariert:
Public Declare Function OSfCreateShellLink Lib "vb6stkit.dll" Alias "fCreateShellLink" (ByVal lpstrFolderName As String, ByVal lpstrLinkName As String, ByVal lpstrLinkPath As String, ByVal lpstrLinkArguments As String, ByVal fPrivate As Long, ByVal sParent As String) As Long
Im Modul basSetup1 finden Sie diese Funktion aufgerufen:
Public Sub CreateShellLink(ByVal strLinkPath As String, _ ByVal strGroupName As String, _ ByVal strLinkArguments As String, _ ByVal strLinkName As String, _ ByVal fPrivate As Boolean, _ sParent As String, _ Optional ByVal fLog As Boolean = True) Dim fSuccess As Boolean ' [...] fSuccess = OSfCreateShellLink(strGroupName, _ strLinkName, _ strLinkPath, _ strLinkArguments, _ fPrivate, _ sParent) ' [...] End Sub
Das Installationsprogramm SETUP1.VBP erlaubt also über die Nutzung einer Funktion aus der beim Setup verwendeten DLL-Datei vb6stkit.dll, eine Verknüpfung anzulegen. Dabei wird an Informationen für die Verknüpfungen verwendet:
lpstrFolderName: Der Name des Verzeichnisses, in dem die Verknüpfung angelegt werden soll, relativ zum Startmenü-Ordner (i.A. die Programmgruppe im Startmenü)
lpstrLinkName: Der Anzeigename für die Verknüpfung
lpstrLinkPath: Der Zielpfad zur verknüpften Datei
lpstrLinkArguments: Die Kommandozeilenargumente für den Aufruf der verknüpften Datei
fPrivate: Die Angabe, ob das benutzerspefizische Startmenü oder das Startmenü für alle Anwender verwendet werden soll
sParent: Der Name des der Programmgruppe übergeordneten Verzeichnisses (i.A. der Platzhalter "$(Programs)" für den Bereich "Programme" des Startmenüs.
Selbstverständlich können Sie sich dieser Funktion auch in Ihren eigenen Anwendungen bedienen: Die Datei vb6stkit.dll dürfen Sie mit Ihren Anwendungen weitergeben (bei Verwendung des "Verpackungs- und Weitergabeassistenten" ist sie sogar zur Ausführung des Setup-Programms zwingend erforderlich).
Tipp für Vorgängerversionen: Das analoge Vorgehen der Installationsanwendungen von Visual Basic 4.0-32 und Visual Basic 5.0 finden Sie, inklusive zugehöriger Deklarationen, im Microsoft Knowledge Base Artikel Nr. 155303 beschrieben: "HOWTO: Create Shortcuts (Shell Links) within Windows".
Jedoch hat dieser Ansatz durchaus mehrere Nachteile: Zum einen war in der Fragestellung verlangt worden, möglichst keine zusätzliche Datei für diesen Zweck mit ausliefern zu müssen. Zum anderen werden Sie spätestens nach Auswahl des Menüpunkts "Eigenschaften" aus dem Windows Explorer-Kontextmenü einer Verknüpfungsdatei feststellen, dass eine Verknüpfung weit mehr Konfigurationsmöglichkeiten anbietet als die, die durch die Funktion fCreateShellLink zur Verfügung gestellt werden. Auch die sehr enge Bindung an das Startmenü ist für allgemeinere Fälle als den eines Setup-Programms eher hinderlich als hilfreich.
Wie die Typbibliothek SHELLLNK.TLB hilft
"Irgendwie", werden Sie vielleicht sagen, "macht das die vb6stkit.dll doch auch - aber wie?". Natürlich ist das Anlegen einer Verknüpfung keine exklusive Spezialität dieser DLL - auch der Windows Explorer selber bietet Ihnen ja die Möglichkeit an, Verknüpfungen anzulegen und sie mit vielfältigen Eigenschaften auszustatten. Und glücklicherweise ist es auch nicht notwendig, das interne Dateiformat einer LNK-Datei zu erforschen, um diese selbst komponieren zu können.
Die Bibliothek StdOle2, fester Bestandteil jedweden frischeren COM-Daseins, stellt diese Möglichkeit über Schnittstellen zur Verfügung. Alles, das zu ihrer Verwendung noch fehlt ist eine Möglichkeit, auf diese Schnittstellen zugreifen zu können - also dem Visual Basic Compiler die Information zu geben, wo und wie diese Schnittstellen anzusprechen sind.
Eine solche Schnittstellenbeschreibung finden Sie in einer Typbibliothek auf Ihrer Visual Basic-CD: Im Verzeichnis \TOOLS\UNSUPPRT\SHELLLNK\ finden Sie die Datei SHELLLNK.TLB sowie ein Demonstrationsprojekt, das eine mögliche Verwendung der darin deklarierten Schnittstellen aufzeigt. Sollten Sie Visual Basic aus Visual Studio verwenden, finden Sie das gleiche Verzeichnis auf der dritten CD im Pfad \common\Tools\vb\Unsupprt\ShellLnk.
Durch einen Verweis auf die Typbibliothek machen Sie dem Compiler die darin definierten Schnittstellen bekannt und können sie ab sofort nutzen. Dabei ist die TLB-Datei für diesen Verweis ausschließlich für den Compiler notwendig, Sie müssen die SHELLLNK.TLB also nicht mit Ihrer compilierten Anwendung ausliefern!
Mithilfe der Schnittstelle ShellLinkA können Sie nun ein Objekt erzeugen, dem Sie die verschiedenen Eigenschaften einer Verknüpfung über dessen mit Set... beginnenden Methoden zuweisen können. Um eine Verknüpfung auf die Festplatte zu speichern, nutzen Sie die polymorphe Zuweisung dieses Objekts an eine Variable vom Typ IPersistFile, über die Sie dann unter Übergabe des Dateipfades für Ihre Verknüpfung als Unicode-String die Methode Save aufrufen.
Ebenso können Sie auch den Rückwärtsgang einschalten, auch wenn der ein wenig beschwerlicher ist: Um die Daten einer Verknüpfungsdatei auszulesen, erzeugen Sie ein Objekt vom Typ ShellLinkA und weisen es wiederum polymorph einer Variablen vom Typ IPersistFile zu. Den Pfad zur Verküpfungsdatei geben Sie in der Load-Methode wiederum als Unicode-String an. Für den weiteren Ablauf sprechen Sie das Objekt wieder über die ShellLinkA-Schnittstelle an: Ihre mit Get... beginnenden Methoden müssen Sie mit ausreichend großen reservierten Speicherbereichen (bei Strings im einfachsten Fall mit vorbelegten Strings) versorgen. Nach dem Aufruf ist dann der erhaltene String vor dem ersten Nullzeichen (vbNullChar) abzuschneiden. Beim Abholen numerischer Werte ist dieser Aufwand natürlich nicht erforderlich.
Eine kleine Besonderheit betrifft den Wert der Tastenkombination für die Verknüpfung: Die Bedeutung seines Werts teilt sich in zwei Bereiche auf. Im ersten Byte finden Sie den Tastencodewert für die Buchstabentaste der Tastenkombination. Die Werte für die gleichzeitige Betätigung der Alt-, Steuerungs- und Umschalttasten finden sie im darüber liegenden Byte vor. In den Funktionen LoadLink und SaveLink des nachfolgenden Beispielcodes finden Sie diese kleine Besonderheit behandelt und auf zwei Eigenschaften der Beispielklasse verteilt.
Finale: Eine komfortable Klassenlösung zum Loslegen
Die zur Typbibliothek gehörende Beispielanwendung auf der Visual Basic-CD ist ein netter Anfang, um die SHELLLNK.TLB zu nutzen. Dennoch ist sie eher unpraktisch in der Handhabung (wenn auch nicht so deutlich wie die direkte Nutzung der Typbibliothek) und nutzt die Möglichkeiten, die die Typbibliothek anbietet, auch nicht voll aus.
Hier soll Ihnen daher zum Abschluss eine Klassenlösung an die Hand gegeben werden, deren einfachere Handhabung und umfangreichere Nutzungsmöglichkeit die Aufgabe des Auslesens und Anlegens von Verknüpfungen deutlich erleichtert. Auch wenn der Code auf den ersten Blick umfassend erscheint (der größte Teil davon betrifft, wie so oft, lediglich stupide Eigenschaftenprozeduren) - schauen Sie ruhig einmal in die Funktionen LoadLink und SaveLink herein: Dort finden Sie die eigentliche Arbeit mit der Typbibliothek.
Tipp: Kopieren Sie den gesamten Quelltext zunächst in WRITE.EXE und erst von dort aus in Ihre Entwicklungsumgebung, um den Verlust von Zeilenumbrüchen zu vermeiden.
Option Explicit ' ----------------------------------------------------------- ' Klassenmodul cShellLink ' ' Dient dem Erzeugen und dem Auslesen von Windows- ' Verknüpfungsdateien (*.lnk) ' ' Voraussetzung: Verweis auf die Typbibliothek SHELLLNK.TLB! ' Sie finden diese Datei auf Ihrer Visual Basic- oder ' Visual Studio-CD: ' Visual Basic-CD: \Tools\Unsupprt\ShellLnk ' Visual Studio-CD 3: \common\Tools\vb\Unsupprt\ShellLnk ' Die Typbibliothek wird nur für das Compilieren benötigt: ' Eine Auslieferung mit Ihrer Anwendung ist unnötig! ' ----------------------------------------------------------- ' Um Informationen einer bestehenden Verknüpfungsdatei zu ' ermitteln, belegen Sie die FileName-Eigenschaft dieser ' Klasse entsprechend und führen Sie dann die Funktion ' LoadLink aus. Um eine Verknüpfungsdatei zu erstellen, ' belegen Sie die gewünschten Eigenschaften mit Werten, ' vergeben Sie an die FileName-Eigenschaft einen Pfad für ' die neue Verknüpfung und führen Sie die Funktion SaveLink ' aus. ' ----------------------------------------------------------- ' Einen einfacheren Weg, Verküpfungen weniger flexibel zu ' erstellen, finden Sie in der Microsoft Knowledge Base: ' HOWTO: Create Shortcuts (Shell Links) within Windows ' <A href="https://support.microsoft.com/default.aspx?scid=kb;DE;155303">https://support.microsoft.com/default.aspx?scid=kb;DE;155303</A> ' ----------------------------------------------------------- ' Copyright (c) 2003 by Mathias Schiffer, AixSoft Software. ' Downloadmöglichkeit: <A href="http://www.aixsoft.de/msdn/quickie">http://www.aixsoft.de/msdn/quickie</A> ' ----------------------------------------------------------- ' ---- BENÖTIGTE DEKLARATIONEN ---- ' Werte für die Eigenschaft StartupWindowState Public Enum StartupWindowStates swsNormal = 1 swsMaximized = 7 swsMinimized = 3 End Enum ' Eigenschaftenvariable Private mDescription As String Private mPath As String Private mRelativePath As String Private mWorkingDirectory As String Private mArguments As String Private mStartupWindowState As StartupWindowStates Private mHotkey As VBRUN.KeyCodeConstants Private mHotkeyModifiers As VBRUN.ShiftConstants Private mIconPath As String Private mIconNumber As Long Private mFileName As String ' API-Deklarationen zur Ermittlung spezieller Pfade Public Enum ShellFolders CSIDL_DESKTOP = &H0& CSIDL_PROGRAMS = &H2& CSIDL_CONTROLS = &H3& CSIDL_PRINTERS = &H4& CSIDL_PERSONAL = &H5& CSIDL_FAVORITES = &H6& CSIDL_STARTUP = &H7& CSIDL_RECENT = &H8& CSIDL_SENDTO = &H9& CSIDL_BITBUCKET = &HA& CSIDL_STARTMENU = &HB& CSIDL_DESKTOPDIRECTORY = &H10& CSIDL_DRIVES = &H11& CSIDL_NETWORK = &H12& CSIDL_NETHOOD = &H13& CSIDL_FONTS = &H14& CSIDL_TEMPLATES = &H15& CSIDL_COMMON_STARTMENU = &H16& CSIDL_COMMON_PROGRAMS = &H17& CSIDL_COMMON_STARTUP = &H18& CSIDL_COMMON_DESKTOPDIRECTORY = &H19& CSIDL_APPDATA = &H1A& CSIDL_PRINTHOOD = &H1B& End Enum Private Type SHITEMID cb As Long abID As Byte End Type Private Type ITEMIDLIST mkid As SHITEMID End Type Private Declare Function SHGetSpecialFolderLocation _ Lib "shell32.dll" ( _ ByVal hwndOwner As Long, _ ByVal nFolder As Long, _ ByRef pidl As ITEMIDLIST _ ) As Long Private Declare Function SHGetPathFromIDList _ Lib "shell32.dll" Alias "SHGetPathFromIDListA" ( _ ByVal pidl As Long, _ ByVal pszPath As String _ ) As Long ' ---- EIGENSCHAFTENPROZEDUREN ---- Public Property Let FileName(ByVal NewData As String) ' Dateiname für die Verknüpfungsdatei (*.lnk). mFileName = NewData End Property Public Property Get FileName() As String FileName = mFileName End Property Public Property Let IconNumber(ByVal NewData As Long) ' Ressourcenposition des Icons in der durch IconPath ' repräsentierten Datei. mIconNumber = NewData End Property Public Property Get IconNumber() As Long IconNumber = mIconNumber End Property Public Property Let IconPath(ByVal NewData As String) ' Pfad zu einer Icondatei (*.ico) oder zu einer Datei, ' die Icons als Ressourcen enthält. mIconPath = NewData End Property Public Property Get IconPath() As String IconPath = mIconPath End Property Public Property Let Hotkey( _ ByVal NewData As VBRUN.KeyCodeConstants) ' KeyCode der Taste, die im Zusammenhang mit den durch ' HotkeyModifiers beschriebenen Sondertasten als ' Tastenkombination verwendet werden soll. mHotkey = NewData End Property Public Property Get Hotkey() As VBRUN.KeyCodeConstants Hotkey = mHotkey End Property Public Property Let HotkeyModifiers( _ ByVal NewData As VBRUN.ShiftConstants) ' Sondertasten (Strg, Shift und Alt), die zusammen mit der ' durch HotKey repräsentierten Taste als Tastenkombination ' verwendet werden. mHotkeyModifiers = NewData End Property Public Property Get HotkeyModifiers() As VBRUN.ShiftConstants HotkeyModifiers = mHotkeyModifiers End Property Public Property Let StartupWindowState( _ ByVal NewData As StartupWindowStates) ' Festgelegter Öffnungszustand des Startfensters der Anwendung. ' Die Anwendung muss diese Möglichkeit der Einstellung ' unterstützen. mStartupWindowState = NewData End Property Public Property Get StartupWindowState() As StartupWindowStates StartupWindowState = mStartupWindowState End Property Public Property Let Arguments(ByVal NewData As String) ' Befehlszeilenargumente für den Aufruf der Anwendung. mArguments = NewData End Property Public Property Get Arguments() As String Arguments = mArguments End Property Public Property Let RelativePath(ByVal NewData As String) ' Relativer Pfad zur Anwendung. mRelativePath = NewData End Property Public Property Get RelativePath() As String RelativePath = mRelativePath End Property Public Property Let WorkingDirectory(ByVal NewData As String) ' Verzeichnis, das als Arbeitsverzeichnis verwendet werden soll. mWorkingDirectory = NewData End Property Public Property Get WorkingDirectory() As String WorkingDirectory = mWorkingDirectory End Property Public Property Let Path(ByVal NewData As String) ' Pfad zur verknüpften Anwendungsdatei. mPath = NewData End Property Public Property Get Path() As String Path = mPath End Property Public Property Let Description(ByVal NewData As String) ' Beschreibung der Verknüpfung mDescription = NewData End Property Public Property Get Description() As String Description = mDescription End Property ' ---- CLASS INITIALIZE / TERMINATE ---- Private Sub Class_Initialize() mStartupWindowState = swsNormal ' Standardwert End Sub Private Sub Class_Terminate() ' Keine Aufräumarbeiten erforderlich. End Sub ' ---- ÖFFENTLICHE FUNKTIONEN ---- Public Function LoadLink() As Boolean ' Lädt Informationen aus einer Verknüpfungsdatei und ' legt sie im cShellLink-Objekt ab. ' Rückgabewert: True bei Erfolg. ' Hinweis: Funktion löst im Bedarfsfall Fehler aus! Const INFOTIPSIZE As Long = 1024 Const MAX_PATH As Long = 260 Dim PersistFile As IShellLinkA.IPersistFile Dim ShellLink As IShellLinkA.ShellLinkA Dim FD As IShellLinkA.WIN32_FIND_DATA Dim strBuffer As String Dim lFlags As Long ' Existiert die angegebene Datei? If LenB(Dir$(mFileName)) = 0 Then Err.Raise 53, "cShellLink.LoadLink" ' File not found End If ' Mithilfe von IPersistFile Informationen laden Set ShellLink = New IShellLinkA.ShellLinkA Set PersistFile = ShellLink PersistFile.Load StrConv(mFileName, vbUnicode), 0 Set PersistFile = Nothing strBuffer = Space$(INFOTIPSIZE) ' Speicherreservierung With ShellLink ' Die Verknüpfungsbeschreibung ermitteln .GetDescription strBuffer, INFOTIPSIZE mDescription = NullTrim(strBuffer) ' Den Pfad zum Verknüpfungsziel ermitteln .GetPath strBuffer, MAX_PATH, FD, lFlags mPath = NullTrim(strBuffer) ' Das Arbeitsverzeichnis ermitteln .GetWorkingDirectory strBuffer, INFOTIPSIZE mWorkingDirectory = NullTrim(strBuffer) ' Die Aufrufargumente ermitteln .GetArguments strBuffer, INFOTIPSIZE mArguments = NullTrim(strBuffer) ' Dateinamen und Icon-Nummer für das ' Verknüpfungsicon ermitteln .GetIconLocation strBuffer, INFOTIPSIZE, mIconNumber mIconPath = NullTrim(strBuffer) ' Tastenkombination ermitteln .GetHotkey mHotkey mHotkeyModifiers = mHotkey \ &H100& ' HiByte = Modifiers mHotkey = mHotkey And &HFF& ' LoByte = Taste ' Initialen Fensterstil beim Ausführen ermitteln .GetShowCmd mStartupWindowState End With LoadLink = True End Function Public Function SaveLink( _ Optional ByVal Overwrite As Boolean = True) As Boolean ' Speichert die Informationen des cShellLink-Objekts ' als Verknüpfung unter dem in FileName angegebenen Pfad ab. ' Setzen Sie Overwrite auf False, wenn Sie eine bereits ' bestehende Datei nicht überschreiben wollen. ' Rückgabewert: True bei Erfolg. ' Hinweis: Funktion löst im Bedarfsfall Fehler aus! Dim PersistFile As IShellLinkA.IPersistFile Dim ShellLink As IShellLinkA.ShellLinkA ' Prüfen, ob die FileName-Eigenschaft belegt wurde If LenB(mFileName) = 0 Then Err.Raise 52, "cShellLink.SaveLink" ' Bad file name or number End If ' Prüfen, ob die in FileName angegebene Datei existiert If LenB(Dir$(mFileName, vbHidden)) > 0 Then If Overwrite = False Then Err.Raise 58, "cShellLink.SaveLink" ' File already exists End If End If Set ShellLink = New IShellLinkA.ShellLinkA ' IShellLink-Objekt mit Informationen versorgen With ShellLink .SetDescription mDescription .SetPath mPath .SetRelativePath mRelativePath, 0 .SetWorkingDirectory mWorkingDirectory .SetArguments mArguments .SetHotkey CInt(mHotkey + &H100 * mHotkeyModifiers) .SetIconLocation mIconPath, mIconNumber .SetShowCmd mStartupWindowState End With ' Informationen mithilfe von IPersistFile abspeichern Set PersistFile = ShellLink lPersistFile.Save StrConv(mFileName, vbUnicode), 0 SaveLink = True End Function Public Function ModifiersDescription( _ ByVal Modifiers As VBRUN.ShiftConstants) As String ' Gibt eine deutschsprachige Klartextbeschreibung für ' einen HotkeyModifiers-Wert zurück (z.B. "Strg + Alt") ' Ist eine Steuerungs-Taste mit im Spiel? If CBool(Modifiers And vbCtrlMask) Then ' "Strg" anfügen ModifiersDescription = "Strg" End If ' Ist eine Shift-Taste mit im Spiel? If CBool(Modifiers And vbShiftMask) Then ' Bei Bedarf " + " hinzufügen If LenB(ModifiersDescription) Then ModifiersDescription = ModifiersDescription & " + " End If ' "Umschalt" anfügen ModifiersDescription = ModifiersDescription & "Umschalt" End If ' Ist eine Alt-Taste mit im Spiel? If CBool(Modifiers And vbAltMask) Then ' Bei Bedarf " + " hinzufügen If LenB(ModifiersDescription) Then ModifiersDescription = ModifiersDescription & " + " End If ' "Alt" anfügen ModifiersDescription = ModifiersDescription & "Alt" End If End Function Public Function GetSystemFolderPath( _ ByVal FolderID As ShellFolders) As String ' Ermittelt den Pfad zu bestimmten Systemordnern. Dim ItemList As ITEMIDLIST Dim sPath As String If SHGetSpecialFolderLocation(0, FolderID, ItemList) = 0 Then sPath = Space$(260) If SHGetPathFromIDList(ItemList.mkid.cb, sPath) <> 0 Then GetSystemFolderPath = Left$(sPath, _ InStr(sPath, vbNullChar) - 1) End If End If End Function ' ---- PRIVATE HILFSFUNKTIONEN ---- Private Function NullTrim( _ ByRef StringToBeTrimmed As String) As String ' Gibt den Teil des übergebenen Strings zurück, der links vom ' ersten im String vorkommenden Nullzeichen (vbNullChar) steht. ' Kommt kein Nullzeichen vor, wird der übergebene String als ' Rückgabewert verwendet. Dim lPos As Long ' Verwendung als Position eines Zeichens ' Erstes vorkommendes Nullzeichen suchen: lPos = InStr(StringToBeTrimmed, vbNullChar) If lPos > 0 Then ' Teilstring vor dem Nullzeichen zurückgeben: NullTrim = Left$(StringToBeTrimmed, lPos - 1) Else ' Mangels Nullzeichen den kompletten String zurückgeben: NullTrim = StringToBeTrimmed End If End Function ' ---------------------------------