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.

Newsgroups0304_01.gif

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