FTPFindFirstFile, InternetFindNextFile und FTP-Session
Hier geht es um die Frage warum werden keine Unterverzeichnisse auf dem FTP-Server durchsucht?
Eigentlich ist es kurz erklärt. Wer sich mit
rekursiven Funktionen auskennt, um Verzeichnisse
und Unterverzeichnisse zu durchsuchen, wird sich wundern, warum es auf dem FTP-Server nicht geht.
Das Stichwort heißt
Session. Jedes mal, wenn ein Verzeichnis auf dem FTP-Server geöffnet wird,
ist die
Session an dieses Verzeichnis gebunden. Wenn nun das FTP-Verzeichnis gewechselt werden
soll, muß vorab die
FTP-Session beendet werden mit
InternetCloseHandle(FileHandel).
Dann mit
FtpSetCurrentDirectory() das Verzeichnis wechseln und mit
FTPFindFirstFile()
das Verzeichnis öffnen und ein neues Handle holen. Das muß natürlich auch passieren,
wenn die Funktion wieder Werte vom
Stack holt. Einfach mal einen kleinen Verzeichniszweig
ein bisschen
Debuggen, dann versteht man schon.
Hinweis: Wenn ihr sehr viele Verzeichnisse durchsuchen müsst, zb. mehr als mindesten den Verzeichnisbaum
von XP, kann es zu einem
Stacküberlauf kommen.
// Nach Dateien und Verzeichnisen suchen rekursiv für FTP-Sitzungen,
// wichtig ist immer, das Sitzungs-Handle vor dem Verzeichniswechsel zu schliessen
// und ein neues Handel für die nächste Session zu holen.
function FTPSuchen(ServerPath, SuchFile: string): Boolean;
var findData: WIN32_FIND_DATA;
hwFile: HINTERNET;
j, m, n: Integer;
begin
result := False;
n := 0;
if ServerPath[Length(ServerPath)]<> '/' then
ServerPath := ServerPath + '/';
Application.ProcessMessages;
if Abrechen then exit;
FtpSetCurrentDirectory(hConnect, PChar(Serverpath));
// FTPFindFirstFile sucht in einem Verzeichnis nach dem ersten
// Vorkommen einer bestimmten Datei. Statt INTERNET_FLAG_DONT_CACHE
// kann auch Null gesetzt werden.
hwFile := FTPFindFirstFile(hConnect, '*.*', finddata, INTERNET_FLAG_DONT_CACHE, 0);
if (hwFile <> nil) then begin
repeat // Wenn es ein Verzeichnis ist
if findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY then begin
if findData.cFileName[0] <> '.' then begin //prüfen ob Datei oder Ordner
// Damit es keine Endlosschleife wird, wenn vom Stack zurück geprungen
// wird, und nicht der erste Ordner wieder aufgerufen wird.
if lstrcmp(findData.cFileName, PChar(DirMerken)) > 0 then begin
// Vergleichen und wenn gefunden, dann Funktion für Ausgabe aufrufen
if pos(SuchFile, findData.cFileName) > 0 then
SearchOut(ServerPath, findData, True); // Bei Ordner True übergeben
InternetCloseHandle(hwFile);
result := True;
inc(DirIndex);
// Den Ordner vor dem aktuellen Ordner merken
DavorDir[DirIndex] := findData.cFileName;
DirMerken := '';
// Wenn ein weiteres Verzeichnis gefunden wurde, dann Sich selbst aufrufen
// Pfad und Datei-Name übergeben
FTPSuchen(ServerPath + findData.cFileName, SuchFile);
end; // if lstrcmp
end; // if findData.cFileName[0]
end // if findData.dwFileAttributes
else begin // wenn FILE_ATTRIBUTE_NORMAL = $00000080 = 128;
if lstrcmp(findData.cFileName, PChar(DirMerken)) > 0 then begin
DirMerken := findData.cFileName;
// Vergleichen und wenn gefunden, dann Funktion für Ausgabe aufrufen
if pos(SuchFile, findData.cFileName) > 0 then
SearchOut(ServerPath, findData, False); // Bei Datei False übergeben
end; // if lstrcmp
end; // else
until not InternetFindNextFile(hwFile, @findData) ;
end; // if (hwFile <> nil)
if DirIndex > 0 then begin
// Bei Rücksprung vom Stack den Namen davor übergeben
DirMerken := DavorDir[DirIndex];
DavorDir[DirIndex] := '';
dec(DirIndex);
end;// if DirIndex > 0
m := Length(Serverpath);
if ServerPath[m] = '/' then Delete(Serverpath, m ,1);
// Wenn am Ausgangs-Punkt angekommen
if ServerPath = PfadMerken then begin
Beenden := True;
end;
m := Length(ServerPath);
for j := m downto 1 do begin
if ServerPath[j] = '/' then Break;
n := j;
end; // for j
Delete(ServerPath, n, ((m+1) - n));
// FTP-Session beenden
InternetCloseHandle(hwFile);
FtpSetCurrentDirectory(hConnect, PChar(ServerPath));
// Handle holen von Session davor, vor Rücksprung vom Stack
hwFile := FTPFindFirstFile(hConnect, '*.*', finddata, INTERNET_FLAG_DONT_CACHE, 0);
// Um auch die letzte Sitzung zu schließen, wenn fertig
if Beenden then begin
if (hwFile <> nil) then InternetCloseHandle(hwFile);
end;
end; // proc-end