Spezielle Verzeichnisse ermitteln
Veröffentlicht: 12. Apr 2004 | Aktualisiert: 29. 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:
SHFolder.dll enthält SHGetFolderPath
SHGetFolderPath verwenden
Aus der Visual Basic Newsgroup microsoft.public.de.vb:
Ich stoße immer wieder auf das Problem, dass ich Dateipfade für bestimmte Verzeichnisse ermitteln muss, die es auf den meisten Windows-Rechnern geben sollte. Mal gibt es hierfür einzelne API-Funktionen, mal muss ich diese Pfade kompliziert aus der Registry auslesen. Geht es nicht auch einfacher?
Wer Ordnung hält, muss weniger suchen - dafür aber öfter klicken. Früher wurde alles an Dateien, das sich irgendwie direkt oder indirekt mit Windows in Verbindung bringen ließ, einfach in das Windows- oder das darunter liegende System-Verzeichnis kopiert - bis der Verzeichnisinfarkt drohte. Das Schöne daran war, dass man neben seinem eigenen Programmverzeichnis (App.Path lässt grüßen) eigentlich nur das Windows- und das Systemverzeichnis kennen musste, vielleicht dazu noch ein Temporärverzeichnis. Mit den API-Funktionen GetWindowsDirectory, GetSystemDiretory und GetTempPath war das Thema schnell abgehandelt.
Zwar tragen diese beiden Verzeichnisse auch heute noch jede Menge unüberblickbarer Dateien mit sich, deren Funktionen oft im Verborgenen bleiben. Doch mit jeder neuen Windows-Version kamen neue Ordner hinzu, die nicht nur den Entwicklern, sondern vor allem auch den Anwendern ein Ablagesystem für Dateien erleichtern sollte. Auch die Verlagerung von Benutzerprofilen aus dem Windows-Verzeichnis heraus in das Verzeichnis "Dokumente und Einstellungen" schaffte neben mehr Überblick auch neue Ordner. Von den "Eigenen Dateien" und ihren Unterordnern einmal völlig abgesehen.
Bei so vielen neuen Verzeichnissen, denen jeweils eine spezielle Funktion zukommt, wäre es irgendwann im Lauf der Jahre unübersichtlich geworden, eine API-Funktion pro Pfad zur Verfügung zu stellen. Auch das Auslesen von Pfaden aus der Registry wäre recht umständlich und eventuell sogar von Zugriffsrechteproblemen betroffen. Obwohl Sie in der Registry diverse Ordner mit speziellen Aufgaben hinterlegt finden: Etwa im Zweig HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders fällt Ihnen gleich eine ganze Liste davon in die Hand.
Abbildung 1: Die Pfade diverser spezieller Verzeichnisse sind z.B. der Registrierungsdatenbank zu entnehmen. Aber es geht auch einfacher.
SHFolder.dll enthält SHGetFolderPath
Mehr Sinn macht es, die Ermittlung beliebiger spezieller Pfade unter Windows in einer einzigen, zentralen Funktion zu vereinigen. Schon seit dem Internet Explorer 4.0 stellt Microsoft daher eine Bibliothek namens SHFolder.dll zur Verfügung, die sich der Aufgabe annimmt. In Windows Me und ab Windows 2000 ist die zuständige Funktion SHGetFolderPath zwar auch in der Windows-eigenen Bibliothek Shell32.dll enthalten. Aus Gründen der Rückwärtskompatibilität bleiben aber auch diese und neuere Windows-Versionen mit der Hilfsbibliothek ausgestattet. SHGetFolderPath nimmt Ihnen die Unterscheidung der genutzten Betriebssysteme einfach ab, so dass Sie sich hierum keine Gedanken mehr machen müssen.
Sie können eine freie Version der SHFolder.dll hier von der Microsoft Homepage als selbstinstallierende EXE-Datei herunterladen. Da die Bibliothek keine eigene Benutzerinteraktion durchführt und auch sonst keine Lokalisierungsaspekte betroffen sind, können Sie bedenkenlos die (einzig verfügbare) englischsprachige Variante verwenden.
SHGetFolderPath verwenden
Zur Verwendung der Funktion kann sie wie folgt deklariert werden:
Private Const CSIDL_FLAG_DONT_VERIFY As Long = &H4000& Private Const CSIDL_FLAG_CREATE As Long = &H8000& Private Const CSIDL_FLAG_MASK As Long = &HFF00& Private Const SHGFP_TYPE_CURRENT As Long = 0& Private Const SHGFP_TYPE_DEFAULT As Long = 1& Private Const MAX_PATH As Long = 260& Private Const S_OK As Long = 0& Private Const S_FALSE As Long = 1& Private Const E_INVALIDARG As Long = &H80070057 Private Declare Function SHGetFolderPath _ Lib "shfolder" Alias "SHGetFolderPathA" ( _ ByVal hWndOwner As Long, _ ByVal Folder As Long, _ ByVal hToken As Long, _ ByVal Flags As Long, _ ByVal strPath As String _ ) As Long
Den Parameter hWndOwner der Funktion belegen Sie in aller Regel mit 0. Andernfalls erhält er ein Fensterhandle für einen eventuell notwendig werdenden Dialog zum Aufbau einer DFÜ-Verbindung.
Der Parameter Folder erhält die Information darüber, zu welchem speziellen Verzeichnis der gesuchte Pfad ermittelt werden soll. Mögliche Werte tragen symbolische Konstantennamen, die mit CSIDL_ beginnen. Wegen der großen Anzahl finden Sie die einzelnen Werte im unten folgenden Codebeispiel aufgelistet. Eine besondere Rolle kommt dem Flag-Wert CSIDL_FLAG_CREATE zu: Wird er mittels Or-Verknüpfung dem Folder-Parameter hinzugefügt, so wird das gesuchte Verzeichnis bei Bedarf automatisch erzeugt. Ein kurzer Hinweis: Virtuelle Verzeichnisse können nicht verwendet werden (so bleibt Ihnen beispielsweise ein Papierkorbordner vorenthalten).
Auch den Parameter hToken werden Sie üblicherweise zu 0 setzen. Ihn können Sie andernfalls verwenden, um über ein Zugriffstoken Zugriffsrechte einzuräumen, die nur für einen bestimmten Benutzer erreichbar sind. Für nähere Details konsultieren Sie hier bitte die Dokumentation.
Auch im Fall des Parameters Flags bleibt eine sinnvolle Auswahl eher bescheiden: Er bestimmt, ob Sie den tatsächlichen (SHGFP_TYPE_CURRENT) oder den nach einer Windowsinstallation üblichen Pfad (SHGFP_TYPE_DEFAULT) eines gesuchten Verzeichnisses zurück erhalten. Relevant ist dies zudem nur in solchen Fällen, in denen ein spezieller Ordner auch umbenannt werden kann. Gelegenheiten, in denen Sie den Wert SHGFP_TYPE_DEFAULT benötigen, dürften deutlich in der Minderheit sein.
Letztlich ist der Funktion im Parameter strPath ein vorbelegter Stringbuffer zu übergeben, in den die Funktion den gesuchten Pfad als Rückgabewert einbringen kann. Sinnvoll ist hier natürlich ein String der vorbelegten Länge MAX_PATH.
Der Rückgabewert von SHGetFolderPath kann verschiedene Werte annehmen. Ist die Funktion erfolgreich ausgeführt worden, so ist der Rückgabewert 0 (oder S_OK). Im Fehlerfall erhalten Sie einen von 0 unterschiedlichen Wert, wobei S_FALSE für einen nicht existierenden Ordner und E_INVALIDARG für eine fehlerhafte Bedienung der Funktion steht.
Abschließend finden Sie hier einen komplettierten Beispielcode, der Sie durch die damit angebotene Funktion GetSpecialFolder von Implementierungsinterna befreit (und nebenbei auch die zuvor versprochenen Konstantenwerte aufschlüsselt).
Option Explicit Public Enum CSIDLS CSIDL_DESKTOP = &H0& ' Desktop CSIDL_INTERNET = &H1& ' Internet CSIDL_PROGRAMS = &H2& ' Startmenü: Programme CSIDL_CONTROLS = &H3& ' Systemsteuerung CSIDL_PRINTERS = &H4& ' Drucker CSIDL_PERSONAL = &H5& ' Eigene Dateien CSIDL_FAVORITES = &H6& ' IE: Favoriten CSIDL_STARTUP = &H7& ' Autostart CSIDL_RECENT = &H8& ' Zuletzt benutzte Dokumente CSIDL_SENDTO = &H9& ' Senden an / SendTo CSIDL_BITBUCKET = &HA& ' Papierkorb CSIDL_STARTMENU = &HB& ' Startmenü CSIDL_MYMUSIC = &HD ' Eigene Musik CSIDL_MYVIDEO = &HE ' Eigene Videos CSIDL_DESKTOPDIRECTORY = &H10& ' Desktopverzeichnis CSIDL_DRIVES = &H11& ' Mein Computer CSIDL_NETWORK = &H12& ' Netzwerk CSIDL_NETHOOD = &H13& ' Netzwerkumgebung CSIDL_FONTS = &H14& ' Windows\Fonts CSIDL_TEMPLATES = &H15& ' Vorlagen CSIDL_COMMON_STARTMENU = &H16& ' "All Users" - Startmenü CSIDL_COMMON_PROGRAMS = &H17& ' "All Users" - Programme CSIDL_COMMON_STARTUP = &H18& ' "All Users" - Autostart CSIDL_COMMON_DESKTOPDIRECTORY = &H19& ' "All Users" - Desktop CSIDL_APPDATA = &H1A& ' Anwendungsdaten CSIDL_PRINTHOOD = &H1B& ' Druckumgebung CSIDL_LOCAL_APPDATA = &H1C& ' Lokale Einstellungen\Anwendungsdaten CSIDL_COMMON_FAVORITES = &H1F& ' "All Users" - Favoriten CSIDL_INTERNET_CACHE = &H20& ' IE: Temporäre Internetdateien CSIDL_COOKIES = &H21& ' IE: Cookies CSIDL_HISTORY = &H22& ' IE: Verlauf CSIDL_COMMON_APPDATA = &H23& ' "All Users" - Anwendungsdaten CSIDL_WINDOWS = &H24& ' Windows CSIDL_SYSTEM = &H25& ' Windows\System32 CSIDL_PROGRAM_FILES = &H26& ' C:\Programme CSIDL_MYPICTURES = &H27& ' Eigene Bilder CSIDL_PROFILE = &H28& ' Anwenderprofil (Benutzername) CSIDL_SYSTEMX86 = &H29& ' Windows\System32 CSIDL_PROGRAM_FILES_COMMON = &H2B& ' Gemeinsame Dateien CSIDL_COMMON_TEMPLATES = &H2D& ' "All Users" - Vorlagen CSIDL_COMMON_DOCUMENTS = &H2E& ' "All Users" - Dokumente CSIDL_COMMON_ADMINTOOLS = &H2F& ' "All Users" - Verwaltung CSIDL_ADMINTOOLS = &H30& ' Startmenü\Programme\Verwaltung End Enum Private Const CSIDL_FLAG_DONT_VERIFY As Long = &H4000& Private Const CSIDL_FLAG_CREATE As Long = &H8000& Private Const CSIDL_FLAG_MASK As Long = &HFF00& Private Const SHGFP_TYPE_CURRENT As Long = 0& Private Const SHGFP_TYPE_DEFAULT As Long = 1& Private Const MAX_PATH As Long = 260& Private Const S_OK As Long = 0& Private Const S_FALSE As Long = 1& Private Const E_INVALIDARG As Long = &H80070057 Private Declare Function SHGetFolderPath _ Lib "shfolder" Alias "SHGetFolderPathA" ( _ ByVal hWndOwner As Long, _ ByVal Folder As Long, _ ByVal hToken As Long, _ ByVal Flags As Long, _ ByVal strPath As String _ ) As Long Public Function GetSpecialFolder(ByVal CSIDL As CSIDLS, _ Optional ByVal Create As Boolean = False, _ Optional ByVal Verify As Boolean = False _ ) As String ' Liefert den Pfad zu einem speziellen Verzeichnis zurück. Im Fehlerfall ' wird ein leerer String returniert. Wird Create zu True gesetzt, so wird ' ein abgefragtes Verzeichnis bei Bedarf automatisch angelegt. Setzen Sie ' Verify zu True, um vor der Rückgabe eines Pfades eine Prüfung durchzu- ' führen, dass der Pfad tatsächlich existiert. Dim sPath As String ' Zu ermittelnder Pfad Dim RetVal As Long ' Rückgabewert Dim lFlags As Long ' Eigenschaften ' Stringbuffer füllen sPath = Space$(MAX_PATH) ' Flags-Parameter zusammenstellen lFlags = CSIDL If Create Then ' Bei Bedarf automatisch erzeugen lFlags = lFlags Or CSIDL_FLAG_CREATE End If If Not Verify Then ' Existenz nicht überprüfen lFlags = lFlags Or CSIDL_FLAG_DONT_VERIFY End If ' Pfad zum Verzeichnis ermitteln RetVal = SHGetFolderPath(0, lFlags, 0, SHGFP_TYPE_CURRENT, sPath) ' Erfolgskontrolle und Rückgabe des Ergebnisses Select Case RetVal Case S_OK ' Gültiges Verzeichnis gefunden GetSpecialFolder = Left$(sPath, InStr(1, sPath, vbNullChar) - 1) Case S_FALSE ' S_FALSE ' lCSIDL ist gültig, aber das Verzeichnis existiert nicht Case E_INVALIDARG ' Ungültiges Verzeichnis End Select End Function