Einzelnen Beitrag anzeigen

Benutzerbild von d3g
d3g

Registriert seit: 21. Jun 2002
602 Beiträge
 

Pfad der "Special Folders" ermitteln

  Alt 18. Jul 2002, 15:29
Wie bekomme ich den Pfad von "Special Folders" (Desktop, Favoriten etc.)?

1. Warum so kompliziert?

Die Pfade der angesprochenen "Special Folders" stehen zwar in der Registry, aber laut Microsoft könnten sich die Orte ändern. Daher soll man die Funktion MSDN-Library durchsuchenSHGetSpecialFolderLocation verwenden. Diese Funktion ist aber nicht ganz so einfach und um sie sauber auzuführen muss man sich etwas mit dem COM-Interface IMalloc auseinandersetzen.

2. Die benötigten Units

Folgende Units werden benötigt:

Delphi-Quellcode:
uses
  { ... },
  ActiveX, // IMalloc
  ShellAPI, // SHGetSpecialFolderLocation() und SHGetPathFromIDList()
  ShlObj; // CSIDL_-Konstanten
3. IMalloc, SHGetMalloc()

Die Funktion MSDN-Library durchsuchenSHGetSpecialFolderLocation belegt Speicher um den Path in einer sogennanten ItemIDList zurückzugeben (eine Liste von PIDLs, den Bezeichnern für alle Objekte im Shell Namespace, dem baumförmig aufgebauten Dateisystem wie im Windows-Explorer links zu sehen). Da für diese Liste Speicher von dieser Funktion reserviert wurde, jedoch nicht freigegeben werden kann (weil ein Pointer auf die Liste ja an unser Programm weitergeben werden muss), sind wir für das freigeben des Speichers, wenn wir die Liste nicht mehr benötigen, zuständig. Normalerweise würde man das ja über FreeMem() machen, jedoch weist der Pointer, der auf die Liste zeigt, in einen fremden Adressraum, wir können da also nicht so einfach herumpfuschen. Ein Freigeben mit FreeMem() würde in einer Schutzverletzung resultieren.

Microsoft bietet hier als Lösung das Speicherverwaltungsinterface MSDN-Library durchsuchenIMalloc an. Dessen Methode IMalloc.Free() übergibt man den Pointer auf den freizugebenden Speicher und die Sache ist gegessen.

Um einen Pointer auf die VTable von IMalloc zu bekommen (damit wir die Methode Free() aufrufen können) benutzen wir die Funktion SHGetMalloc. Der folgende Code demonstriert, die man ein IMalloc-Interface zu "fassen" bekommt:

Delphi-Quellcode:
var
  pMalloc: IMalloc;

  // ...

  // get IMalloc interface pointer
  if (SHGetMalloc(pMalloc) <> S_OK) then
  begin
    MessageBox(hWindow, 'Couldn''t get pointer to IMalloc interface.',
               'SHGetMalloc(pMalloc)', 16);
    Exit;
  end;
Nun kann man IMalloc.Free() ganz einfach so aufrufen:

Delphi-Quellcode:
var
  p: PChar;

// ...

pMalloc.Free(p);
4. SHGetSpecialFolderLocation() und SHGetPathFromIDList()

Jetzt kann man mit der Funktion MSDN-Library durchsuchenSHGetSpecialFolderLocation den Pfad zum herauszufindenen Ordner anfordern. Diese Funktion ist wie folgt deklariert:

(C/C++)
Code:
HRESULT SHGetSpecialFolderLocation(
    HWND hwndOwner,
    int nFolder,
    LPITEMIDLIST* ppidl
);
(Object Pascal)
Delphi-Quellcode:
function SHGetSpecialFolderLocation(
  hwndOwner: HWND;
  nFolder: Integer;
  var ppidl: PItemIDList
): HRESULT; stdcall;
Der erste Parameter spezifiziert das Handle des aufrufenden Fensters, das dritte ist der Pointer auf die Item-ID-Liste. Ausschlaggeben für den Ordner, der zurückgegeben wird, ist nFolder. Hier wird eine der folgenden Konstanten übergeben:

Code:
CSIDL_COOKIES             Cookies
CSIDL_DESKTOPDIRECTORY    Desktop
CSIDL_FAVORITES           Favoriten
CSIDL_HISTORY             Internet-Verlauf
CSIDL_INTERNET_CACHE      "Temporary Internet Files"
CSIDL_PERSONAL            Eigene Dateien
CSIDL_PROGRAMS            "Programme" im Startmenü
CSIDL_RECENT              "Dokumente" im Startmenü
CSIDL_SENDTO              "Senden an" im Kontextmenü
CSIDL_STARTMENU           Startmenü
CSIDL_STARTUP             Autostart
Interessant wären auch CSIDL_MYMUSIC, CSIDL_MYPICTURES, CSIDL_SYSTEM, CSIDL_WINDOWS und CSIDL_PROGRAM_FILES, aber diese scheinen in Delphi (noch) nicht deklariert zu sein. Es gibt noch ein paar andere Werte, die virtuelle Ordner spezifieren, aber diese haben keinen Pfad im Dateisystem (z.B. die Netzwerkumgebung).

Hinzu kommt jetzt noch die Funktion MSDN-Library durchsuchenSHGetPathFromIDList, die aus der zurückgebenen Item-ID-Liste den Pfad holt. Die Deklaration:

(C/C++)
Code:
BOOL SHGetPathFromIDList(
    LPCITEMIDLIST pidl,
    LPTSTR pszPath
);
(Object Pascal)
Delphi-Quellcode:
function SHGetPathFromIDList(
  pidl: PItemIDList;
  pszPath: PChar
): Boolean; stdcall;
Jetzt noch ein Anwendungsbeispiel:

Delphi-Quellcode:
var
  pidl: PItemIDList;
  Folder: Integer;
  hWindow: HWND;

  // ...

  SHGetSpecialFolderLocation(hWindow, Folder, pidl);
  GetMem(Path, MAX_PATH);
  SHGetPathFromIDList(pidl, Path);
  Result := Path;
  FreeMem(Path);
5. Die fertige Funktion GetSpecialFolder()

Nun dürfte es relativ einfach sein, eine Funktion daraus zu basteln (weil ich den meisten Code schon oben einkopiert habe *g*):

Delphi-Quellcode:
function GetSpecialFolder(hWindow: HWND; Folder: Integer): String;
var
  pMalloc: IMalloc;
  pidl: PItemIDList;
  Path: PChar;
begin
  // get IMalloc interface pointer
  if (SHGetMalloc(pMalloc) <> S_OK) then
  begin
    MessageBox(hWindow, 'Couldn''t get pointer to IMalloc interface.',
               'SHGetMalloc(pMalloc)', 16);
    Exit;
  end;

  // retrieve path
  SHGetSpecialFolderLocation(hWindow, Folder, pidl);
  GetMem(Path, MAX_PATH);
  SHGetPathFromIDList(pidl, Path);
  Result := Path;
  FreeMem(Path);

  // free memory allocated by SHGetSpecialFolderLocation
  pMalloc.Free(pidl);
end;
Zum Schluss noch ein Anwendungsbeispiel:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetSpecialFolder(Handle, CSIDL_RECENT));
end;
Fertig *g*.

MfG,
d3g

[edit=Chakotay1308][code]- durch [delphi]-Tags ersetzt. Mfg, Chakotay1308[/edit]
[edit=flomei]Wir "räumen auf", daher Titel geändert... Mfg, flomei[/edit]
[edit=Matze]Alte MSDN-Links entfernt und MSDN-Tags gesetzt. MfG, Matze[/edit]
-- Crucifixion?
-- Yes.
-- Good. Out of the door, line on the left, one cross each.
  Mit Zitat antworten Zitat