Discussion:
Pfad zu "Eigene Dateien"
(zu alt für eine Antwort)
Olivier Hess
2006-09-09 09:26:01 UTC
Permalink
Hallo,

Wie kriege ich in Delphi Zugang zu der Variable Pfad "Eigene Dateien"?

Gruss, Olivier
Michael Landenberger
2006-09-09 10:14:34 UTC
Permalink
Post by Olivier Hess
Wie kriege ich in Delphi Zugang zu der Variable Pfad "Eigene Dateien"?
Du suchst die Windows API Funktion SHGetFolderPath in Verbindung mit der
Konstanten CSIDL_MYDOCUMENTS.

Gruß

Michael
Olivier Hess
2006-09-09 10:22:24 UTC
Permalink
Post by Michael Landenberger
Post by Olivier Hess
Wie kriege ich in Delphi Zugang zu der Variable Pfad "Eigene Dateien"?
Du suchst die Windows API Funktion SHGetFolderPath in Verbindung mit der
Konstanten CSIDL_MYDOCUMENTS.
Gruß
Michael
Hallo Michael,

Habe jetzt folgendes eingabaut:

SHGetSpecialFolderLocation(0,CSIDL_APPDATA,pIdl);
SHGetPathFromIDList(pIDL,pIdlPath);
predimpath:=pIdlPath;
If Succeeded(SHGetMalloc(Allocator))Then Begin //Speicher frei
Allocator.Free(pIdL); {$IFDEF VER100}
Allocator.Release;{$ENDIF}end;

Gruss, Oli
Martin Behrens
2006-09-09 19:36:15 UTC
Permalink
Post by Olivier Hess
SHGetSpecialFolderLocation(0,CSIDL_APPDATA,pIdl);
[...]

Das dürften dann aber nicht die Eigenen Dateien sein.


Martin
Olivier Hess
2006-09-09 20:53:59 UTC
Permalink
Post by Martin Behrens
Post by Olivier Hess
SHGetSpecialFolderLocation(0,CSIDL_APPDATA,pIdl);
[...]
Das dürften dann aber nicht die Eigenen Dateien sein.
Martin
Hallo Martin,

ich weiss, aber CSIDL_MYDOCUMENTS kennt Delphi 7 nicht!

Gruss, Oli
Michael Landenberger
2006-09-09 22:14:48 UTC
Permalink
Post by Olivier Hess
ich weiss, aber CSIDL_MYDOCUMENTS kennt Delphi 7 nicht!
Dann versuche es mal mit CSIDL_PERSONAL.

Falls das auch nicht klappt, hier die Konstanten zum manuellen
Nachdeklarieren:

const

CSIDL_PERSONAL = $0005;
CSIDL_MYDOCUMENTS = $000C;

Eine Übersicht über alle CSIDL-Konstanten (in Englisch) findest du hier:

<http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp>

Gruß

Michael
Olivier Hess
2006-09-10 09:36:05 UTC
Permalink
Post by Michael Landenberger
Post by Olivier Hess
ich weiss, aber CSIDL_MYDOCUMENTS kennt Delphi 7 nicht!
Dann versuche es mal mit CSIDL_PERSONAL.
Falls das auch nicht klappt, hier die Konstanten zum manuellen
const
CSIDL_PERSONAL = $0005;
CSIDL_MYDOCUMENTS = $000C;
<http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp>
Gruß
Michael
Hallo Michael,

Danke, mit CSIDL_PERSONAL klappt es!

Gruss, Olivier
Andre Sokolew
2006-09-10 12:34:29 UTC
Permalink
Post by Michael Landenberger
Post by Olivier Hess
Wie kriege ich in Delphi Zugang zu der Variable Pfad "Eigene Dateien"?
Du suchst die Windows API Funktion SHGetFolderPath in Verbindung mit der
Konstanten CSIDL_MYDOCUMENTS.
Warum sollte ich nicht die Registry abfragen nach "Personal" in
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell
Folders]?

Danke für die Antworten.

Andre
Edmund Matzke
2006-09-10 13:20:41 UTC
Permalink
Moin,
Post by Andre Sokolew
Post by Michael Landenberger
Du suchst die Windows API Funktion SHGetFolderPath in Verbindung mit der
Konstanten CSIDL_MYDOCUMENTS.
Warum sollte ich nicht die Registry abfragen nach "Personal" in
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell
Folders]?
weil Microsoft das in in einer neuen Windows-Version mal ändern könnte.

CU, Eddi
Andre Sokolew
2006-09-10 16:54:25 UTC
Permalink
Post by Edmund Matzke
Warum sollte ich nicht die Registry abfragen [...]
weil Microsoft das in in einer neuen Windows-Version mal ändern könnte.
Das meinst Du aber eher prinzipiell und nicht wirklich? Speziell in diesem
Falle ("Personal") kann ich mir das weniger vorstellen als eine Änderung der
Win-API!

Außerdem kennt mein Delphi 5 die Funktion SHGetFolderPath offenbar nicht.
Mit einer eigenen external-Deklaration habe ich den Aufruf dann hinbekommen,
und die Funktion liefert auch das richtige Ergebnis (zum Beispiel in eine
Stringvariable), aber der Versuch, dieses Ergebnis anschließend z.B. mit
Showmessage darzustellen oder auch nur Showmessage('Hallo') scheitern mit
einer unerklärlichen Zugriffsverletzung. Irgend etwas mache ich sicher
falsch...

Die Abfrage der Registry ist vergleichsweise einfach und vor allem risikolos
und funktioniert von Windows 95 bis (mindestens) Vista.

Andre
Michael Landenberger
2006-09-10 17:36:30 UTC
Permalink
Post by Andre Sokolew
Außerdem kennt mein Delphi 5 die Funktion SHGetFolderPath
offenbar nicht.
Das ist ein Delphi-Mangel. Die Funktion ist auf allen Windows-Systemen ab
Windows 95 vorhanden, sofern mindestens der IE 5.0 installiert ist. Mein
D2005 kennt die Funktion allerdings auch nicht. Man kann allerdings auch die
Funktion SHGetSpecialFolderPath verwenden, zumindest die ist bei meinem
D2005 vorhanden.
Post by Andre Sokolew
Mit einer eigenen external-Deklaration habe ich den Aufruf dann
hinbekommen, und die Funktion liefert auch das richtige Ergebnis
(zum Beispiel in eine Stringvariable), aber der Versuch, dieses
Ergebnis anschließend z.B. mit Showmessage darzustellen oder auch
nur Showmessage('Hallo') scheitern mit einer unerklärlichen
Zugriffsverletzung. Irgend etwas mache ich sicher falsch...
Du hast es erkannt ;-) Windows-API-Funktionen können mit Delphi-Strings
nichts anfangen. Du musst einen Puffer bereitstellen und einen Zeiger darauf
(PChar) übergeben. Man kann dafür die bereits erwähnte Funktion
SHGetSpecialFolderPath (die sich für das Vorhaben des OP übrigens auch sehr
gut eignet) als Vorbild nehmen. SHGetSpecialFolderPath ist in ShlObj.pas wie
folgt deklariert:

function SHGetSpecialFolderPath (hwndOwner : HWnd; lpszPath : PAnsiChar;
nFolder: Integer; fCreate : LongBool) : LongBool;

SHGetFolderPath kann entsprechend deklariert werden:

function SHGetFolderPath (hwndOwner : HWnd; nFolder : Integer;
hToken : THandle; dwFlags : DWORD; pszPath : PAnsiChar) : HResult;

Den mit lpszPath bzw. pszPath bezeichneten Puffer muss man vor dem Aufruf
der Funktion reservieren (auf ausreichende Größe achten!) und anschließend
ggf. in einen Delphi-String konvertieren. Dann sollte es auch keine
Schutzverletzungen geben.
Post by Andre Sokolew
Die Abfrage der Registry ist vergleichsweise einfach und vor allem
risikolos und funktioniert von Windows 95 bis (mindestens) Vista.
Ich habe zu Win3.1-Zeiten Programme geschrieben, die unter Windows XP immer
noch einwandfrei laufen. D. h. sie sind kompatibel zu mindestens 7
Windows-Versionen (Win3.1 - Win95 - Win98/SE - WinNT 4.0 - WinME - Win2k -
WinXP). Ich habe stets darauf geachtet, Windows-API-Funktionen zu verwenden.
Sogar ein 16bit-Midi-Sequencer für Windows 3.1 mit ausschließlich
Low-Level-Midiroutinen (nix mit MCI!) läuft dank konsequenter Verwendung von
API-Funktionen auch unter WinXP einwandfrei. Auch wenn ich heute ein
Programm schreibe, mache ich das so, dass es möglichst auf den nächsten 6
Betriebssystemversionen ebenfalls noch läuft. Nur bis Windows Vista in die
Zukunft zu sehen, halte ich daher für ein wenig kurzsichtig ;-)

Pfuschige Software hingegen erkennt man z. B. daran, dass sie auf einem
deutschen Windows plötzlich den Ordner "C:\Program Files" anlegen. Das muss
nicht sein, denn auf deutschen Windowsen heißt dieser Ordner bekanntlich
"C:\Programme". Auch fremdsprachige Programme finden diesen Ordner, wenn sie
die entsprechenden API-Funktionen verwenden, und legen nicht eigenmächtig
einen neuen an.

Gruß

Michael
Andre Sokolew
2006-09-10 18:58:51 UTC
Permalink
Post by Michael Landenberger
Pfuschige Software hingegen erkennt man z. B. daran, dass sie auf einem
deutschen Windows plötzlich den Ordner "C:\Program Files" anlegen. Das
muss nicht sein, denn auf deutschen Windowsen heißt dieser Ordner
bekanntlich "C:\Programme". Auch fremdsprachige Programme finden diesen
Ordner, wenn sie die entsprechenden API-Funktionen verwenden, und legen
nicht eigenmächtig einen neuen an.
Klar. Das leisten die entsprechenden Registry-Einträge aber auch.
Post by Michael Landenberger
Post by Andre Sokolew
Irgend etwas mache ich sicher falsch...
Du hast es erkannt ;-) Windows-API-Funktionen können mit Delphi-Strings
nichts anfangen. Du musst einen Puffer bereitstellen und einen Zeiger
darauf (PChar) übergeben.
Ich hatte das so versucht:
...
interface
...
function SHGetFolderPath(const hwndOwner : HWND;
const nFolder : integer;
const hToken : DWORD;
const dwFlags : DWORD;
const pszPath : PChar) : HRESULT;
stdcall;
external 'shell32.dll' name 'SHGetFolderPathA';

function GetFolderPath(Bezeichnung: string): string;
...
implementation
...
function GetFolderPath(Bezeichnung: string): string;
var
szFolder : PChar;
CSIDL : integer;

begin
szFolder := strAlloc(MAX_PATH + 1);
csidl := 0;
If Bezeichnung = 'Eigene Dateien' then CSIDL := 5; //test

if SHGetFolderPath(0,CSIDL,0,0,szFolder) = S_OK
then result := strPas(szFolder);
Dispose(szFolder);
end;
....................

So, und nachdem ich das alles aufgeschrieben und den Quelltext
herüberkopiert habe, fällt mir doch glatt auf, dass es am Ende nicht Dispose
sondern StrDispose heißen muss.
Dann klappts auch.

Danke für's Durchlesen.

Andre
Hans-Peter Diettrich
2006-09-11 08:44:42 UTC
Permalink
Post by Andre Sokolew
Post by Michael Landenberger
Du hast es erkannt ;-) Windows-API-Funktionen können mit Delphi-Strings
nichts anfangen. Du musst einen Puffer bereitstellen und einen Zeiger
darauf (PChar) übergeben.
function GetFolderPath(Bezeichnung: string): string;
var
szFolder : PChar;
CSIDL : integer;
begin
szFolder := strAlloc(MAX_PATH + 1);
csidl := 0;
If Bezeichnung = 'Eigene Dateien' then CSIDL := 5; //test
if SHGetFolderPath(0,CSIDL,0,0,szFolder) = S_OK
then result := strPas(szFolder);
Dispose(szFolder);
end;
Vorausgesetzt, Result ist ein AnsiString und kein ShortString:

...
SetLength(Result, MAX_PATH); //+1 ist hier nicht notwendig!
...
if SHGetFolderPath(0,CSIDL,0,0,PChar(Result)) = S_OK
then SetLength(Result, strlen(Result));

Das "strlen" ist hier nur ein Anhaltspunkt. Die meisten API Funktionen
liefern die Länge des gespeicherten Strings zurück, die dann direkt in
SetLength eingesetzt werden kann. Falls nicht, so wie hier, kann die
Länge des Strings auch mit Pos(#0, Result)-1 ermittelt werden.

DoDi
Edmund Matzke
2006-09-10 18:17:05 UTC
Permalink
Moin,
Post by Andre Sokolew
Post by Edmund Matzke
Warum sollte ich nicht die Registry abfragen [...]
weil Microsoft das in in einer neuen Windows-Version mal ändern könnte.
Das meinst Du aber eher prinzipiell und nicht wirklich? Speziell in diesem
Falle ("Personal") kann ich mir das weniger vorstellen als eine Änderung der
Win-API!
natürlich eher prinzipiell, hier ist das Risiko wohl eher gering. Aber wenn
man sich das angewöhnt, dann kommt doch irgendwann eine Inkompatibilität
zustande. Und die muss sich nicht unbedingt in einer Exception zeigen,
sondern könnte auch in einem Fehlverhalten des Programms stecken.

Was dann folgt, ist im worst case die Suche nach der Stecknadel im
Heuhaufen: Erst findet man lange nicht heraus, welche Programmfunktion das
merkwürdige Verhalten produziert, dann ist es auf dem eigenen System nicht
reproduzierbar, man hat vielleicht ein anderes Betriebssystem. Und zu guter
letzt kann es dann auch an einem simplen kaputten Patch von MS liegen - das
hatten wir mal mit einem Twain-Treiber, der wegen so einem Patch den
Scanner nicht mehr fand:-(

Wie Michael schon schrieb, möglichst dokumentierte (API-)Funktionen
verwenden, und das Risiko ist weitgehend minimiert.

CU, Eddi
Heiko Nocon
2006-09-10 13:33:27 UTC
Permalink
Post by Andre Sokolew
Warum sollte ich nicht die Registry abfragen nach "Personal" in
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell
Folders]?
Ganz einfach: Weil das Shell-API dokumentiert ist, der Ort der
persistenten Speicherung dieser Informationen aber nicht.

Dokumentierte Schnittstellen sind über die Versionen hinweg sehr viel
stabiler als undokumentierte. Genau deswegen werden sie ja
dokumentiert...
--
Wer Komponenten ohne Quelltext oder richtig miese Komponenten
oder gute Komponenten mit Quelltext, ohne die Source zu verstehen, sich verschafft,
um sie in Form "eigener" Programme in Verkehr zu bringen,
der wird mit Gefängnis nicht unter 5 Jahren bestraft.
NineBerry Schwarz
2006-09-11 11:32:48 UTC
Permalink
Hallo
Post by Olivier Hess
Wie kriege ich in Delphi Zugang zu der Variable Pfad "Eigene Dateien"?
Meine Lösung:
SHGetSpecialFolderLocation mit Registry-Fallback für ältere
Windows-Versionen.

//---------------------------------------------------------------------------
// Funktion GetMyFilesDirectory
// Gibt den Pfadnamen des Ordners "Eigene Dateien" zurück
//---------------------------------------------------------------------------
function GetMyFilesDirectory: string;
begin
Result := GetShellDirectoryEx(CSIDL_PERSONAL, 'Personal');
end;

//---------------------------------------------------------------------------
// Funktion GetShellDirectoryEx
// Gibt den Pfadnamen eines bestimmten Systemordners zurück
// Versucht zuerst, den Ordner anhand der CLSID zu ermitteln.
// Falls das nicht klappt, wird der Wert aus der Registry gelesen
//---------------------------------------------------------------------------
function GetShellDirectoryEx(ID: Integer; RegName: string): string;
var
Reg: TRegistry;
begin
Result:= GetShellDirectory(ID);

if Trim(Result) = '' then
begin
Reg:= TRegistry.Create;
try
Reg.RootKey:= HKEY_CURRENT_USER;
if Reg.OpenKey(REGSTR_PATH_SPECIAL_FOLDERS, False) then
begin
if Reg.ValueExists(RegName) then
Result:= Reg.ReadString(RegName);
end;
finally
Reg.Free;
end;
end;

if Trim(Result) = '' then
begin
Reg:= TRegistry.Create;
try
Reg.RootKey:= HKEY_LOCAL_MACHINE;
if Reg.OpenKey(REGSTR_PATH_SPECIAL_FOLDERS, False) then
begin
if Reg.ValueExists(RegName) then
Result:= Reg.ReadString(RegName);
end;
finally
Reg.Free;
end;
end;
end;

//---------------------------------------------------------------------------
// Funktion GetShellDirectory
// Gibt den Pfadnamen eines bestimmten Systemordners anhand der
// CLSID zurück
//---------------------------------------------------------------------------
function GetShellDirectory(ID: Integer): string;
var
S: string;
ItemIDList: PItemIDList;
SystemHeap: IMalloc;
begin
Result := EmptyStr;

if SHGetSpecialFolderLocation(Forms.Application.Handle, ID,
ItemIDList) = NOERROR then
begin
try
SetLength(S, MAX_PATH);
if SHGetPathFromIDList(ItemIDList, PChar(S)) then
begin
Result:= Copy(S, 1, Pos(#0, S) - 1);
end;
finally
// Von der Shell reservierten Speicher freigeben
if SHGetMalloc(SystemHeap) = NOERROR then
begin
SystemHeap.Free(ItemIDList);
end;
end;
end;
end;

Loading...