Entwickler-Ecke

Dateizugriff - mehrere Dateien verschieben


kradon - Mi 31.07.02 15:57
Titel: mehrere Dateien verschieben
Hallo zusammen,

ich habe ein Problem und zwar habe ich ein Verzeichnis

c:\dateien\

darin stehen folgende Dateien

test1.txt, test2.txt, test3.txt und test4.doc

ich möchte nun alle Dateien mit der Endung .txt in das Verzeichnis

d:\archiv\

verschieben (Quelldatei soll nach dem verschieben nicht mehr vorhanden sein).

So und um das Chaos perfekt zu machen können beliebig viele Dateien
mit beliebig vielen Endungen in dem Verzeichnis c:\dateien\ stehen.

Vielen Dank im voraus.

Gruß
Karsten


Klabautermann - Mi 31.07.02 17:10

Hallo,

die richtigen Dateien finden kannst du mit FindFirst, FindNext und FindClose. Verschieben kannst du mit der API funktion MoveFile.

Gruß
Klabautermann


Christian S. - Mi 31.07.02 17:22

So geht es auch ohne API. Ist allerdings (wahrscheinlich) etwas umständlicher, ich kenne die API-Funktion nicht. Meine Delphi-Hilfe weigert sich, sie zu finden! Frechheit!


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
VAR datei : tSearchRec;
    erfolg : Boolean;
begin
  erfolg:=(FindFirst(quellverzeichnis+'\*.txt',faAnyFile,datei)=0);
  while erfolg do
  begin
    CopyFileTo(quellverzeichnis+'\'+datei.name,zielverzeichnis+'\'+datei.name);
    DeleteFile(quellverzeichnis+'\'+datei.name);
    erfolg := (FindNext(datei)=0);
  end;
  FindClose(datei);
end;


Wichtig: "CopyFileTo" steht in der Unit "IdGlobal"

MfG,
Peter


Klabautermann - Mi 31.07.02 17:45

Peter Lustig hat folgendes geschrieben:
Meine Delphi-Hilfe weigert sich, sie zu finden! Frechheit!

Weil sie in der API-Hilfe beschrieben ist. gebe mal MoveFile ein und drücke F1 (nicht Hilfe aufrufen und dann MoveFile eingeben). Ebenfalls sehr interessant (in diesem zusammenhang) CopyFile und DeleteFile.

Zitat:
BOOL MoveFile(

LPCTSTR lpExistingFileName, // address of name of the existing file
LPCTSTR lpNewFileName // address of new name for the file
);


Der aufruf sieht also so aus:

Quelltext
1:
2:
IF MoveFile(pChar('C:\Autoexec.Bak'), pChar('C:\tmp\Autoexec.Bak')) THEN
  ShowMessage('Datei erfolgreich verschoben');


Das ganze ist übrigens auch in der Windows.Pas importiert, so das du in Formularunits nicht mal deine Uses-Klauses erweitern musst.

Gruß
Klabautermann


Christian S. - Mi 31.07.02 18:55

Hi!

Danke! So klappt das Anzeigen der Hilfe. Kannst Du mir sagen, wo ich eine Liste der API-Funktionen runterladen kann? (Oder wie ich die Informationen, die ja irgendwo auf meinem Rechner gespeichert sein müssen, auslesen kann?)

MfG,
Peter


Klabautermann - Mi 31.07.02 19:01

Hallo,

die Hife ist in deinem Startbutton eingetragen ;). Start->Programme->{Delphi}->Hilfe->Microsoft SDK.

Gruß
Klabautermann


Christian S. - Mi 31.07.02 19:12

Peinlich! :oops:

Danke,
Peter


GPF - Mi 31.07.02 19:55

Oder benutze doch gleich die Windows API Routinen zum Verschieben von Dateien:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
function SHFileMove(Owner:Integer; FromFile, ToFile:string; Flags: word):boolean;
  function ConvertFile(FileName:string):PChar;
  var NewFile:string;
  begin
    NewFile:=Filename;
    while pos(';', NewFile)>0  do
       NewFile[pos(';', NewFile)]:=#0;
    NewFile:=NewFile+#0+#0;
    Result:=PChar(NewFile);
  end;
const Aborted:boolean=true;
var Struct: TSHFileOpStructA;
begin
  with Struct do begin
    wnd:=Owner;
    wFunc:=FO_Move;
    PFrom:=ConvertFile(FromFile);
    pTo:=PChar(ToFile);
    fFlags:=Flags;
    fAnyOperationsAborted:=Aborted;
    hNameMappings:=nil;
    lpszProgressTitle:=nil;
  end;
  Result:=(SHFileOperationA(Struct)=0) and (not aborted);
end;


Ich habe die Funktion so angepaßt, daß sie genau Deinen Bedürfnissen gerecht wird. Da eine einzige Methode unter Windows sämtliche Dateioperationen durchführt (Kopieren, Verschieben, Löschen) sind die Parameter etwas schwierig zu handeln. Anstatt Wildcards zu nutzen kannst Du auch mehrere Dateien durch Semikolon getrennt als Quelle angeben. Windows arbeitet intern mit #0. Die Methode Convertfiles konvertiert alle ; in #0 und fügt an das Ende zweimal #0 an, damit Windows das Ende der Dateiliste erkennt.
Als Flagkomination empfehle ich Dir FOF_FILESONLY or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT. Genaueres dazu kannst Du in dem Windows SDK (nicht die Delphi Hilfe - wird aber mit Delphi ausgeliefert) unter dem Schlüsselwort SHFileOperation nachlesen.
Owner kannst Du auf 0 setzen, sofern Du nicht den Windows eigenen Verschieben/Kopieren/Löschen-Dialog angezeigt bekommen möchtest (Flag FOF_SILENT zeigt diesen Dialog nicht an). Nimm ansonsten das Fensterhandle.

Als Beispiel zum Verschieben aller Textdateien benutze folgenden Einzeiler:

Quelltext
1:
SHFileMove(Handle, 'c:\Quellordner\*.txt', 'c:\Zielordner', FOF_FILESONLY or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT);                    


Christian S. - Mi 31.07.02 20:19

Irgendwie glaube ich, dass die Methode von Klabautermann sehr viel einfacher ist. Wenn Du im Quelltext, den ich weiter oben geschrieben habe, "CopyFileTo" und "DeleteFile" durch die API-Funktion "MoveFile", wie von Klabautermann beschrieben, ersetzt, hast Du einen ziemlich kompakten und einfach verständlichen Code.

MfG,
Peter


GPF - Mi 31.07.02 20:29

Das ist wahr. Allerdings sollen die alten aus dem 16 bit Windows stammenden Funktionen auf Dauer ersetzt werden. Laut den jetzigen Ankündigungen von MS könnte dies schon beim nächsten Windows der Fall sein.

Abgesehen davon bin ich mir nicht sicher ob MoveFile, CopyFile, DeleteFile korrekt mit Wildcards und Unterverzeichnissen korrekt klar kommt.


Christian S. - Mi 31.07.02 20:54

Zitat:

Abgesehen davon bin ich mir nicht sicher ob MoveFile, CopyFile, DeleteFile korrekt mit Wildcards

Mit Wildcards müssen diese Funktionen bei dem gegebenen Code nicht zusammen arbeiten. Ob sie das überhaupt können, weiß ich nicht, wäre aber interessant nachzuschauen/auszuprobieren.

Zitat:
und Unterverzeichnissen korrekt klar kommt.

Was meinst Du damit?

MfG,
Peter


GPF - Mi 31.07.02 21:13

SHFileOperation akzeptiert auch Wildcards - damit wird Deine Dateiiterierungsroutine überflüssig.
Als Quell und Zielparameter akzeptiert diese Windows Routine auch Verzeichnisse. Es wäre ohne Probleme auch möglich zusätzlich einen Ordner zum Verschieben anzugeben. Der Code würde dann wie folgt aussehen:


Quelltext
1:
SHFileMove(Handle, 'c:\Quellordner\*.txt;c:\Quellordner\MoveMeFolder', 'c:\Zielordner', FOF_FILESONLY or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_SILENT);                    


Damit werden also einerseits alle Textdateien aus dem Ordner c:\Quellordner\ verschoben - andererseits aber auch der Ordner MoveMeFolder inklusive aller Unterordner und Dateien. Ich bin mir nicht sicher ob MoveFile dies ebenso korrekt macht.


Klabautermann - Mi 31.07.02 23:46

Ja,

SHFileOperation ist um einiges mächtiger und Komplizierten. Was mich am mesten dran stört sind die Animationen. Lassen die sich unterdrücken?

Gruß
Klabautermann


GPF - Do 01.08.02 04:47

Natürlich, wie ich bereits gesagt habe kann man durch das Flag FOF_SILENT die Animation unterdrücken.


Klabautermann - Do 01.08.02 09:25

GPF hat folgendes geschrieben:
Natürlich, wie ich bereits gesagt habe kann man durch das Flag FOF_SILENT die Animation unterdrücken.

:oops: Peinlich, habe deine obriges Posting nur überflogen.
Ich schätze dann werde ich mir bei gelegenheit mal einfache Copy, move ... Befehle auf dieser Basis bauen.

Gruß
Klabautermann


kradon - Do 01.08.02 10:07
Titel: WOW
Also Leute,

erst mal vielen Dank für die ganzen Antworten, aber wenn ich mir hier euren Quellcode so anschaue komme ich mir ziemlich unwissend vor. *g*

Wäre nett wen jemand von euch einmal den Code mit ausführlichen Komentaren für Dummies versehen kann.

Gruß
Karsten


Christian S. - Do 01.08.02 12:06

Hi!

Eigentlich bin ich inzwischen auch der Meinung, dass der Code von GPF der Bessere ist, aber leichter ist mein Code mit der Ergängzung von Klabautermann.
Wenn Du den Code haben möchtest, kann ich Dir den gerne noch mal mit Kommentaren geben. Aber wie gesagt, ich glaube der von GPF ist besser.

Problem: ich bekomme ihn nicht ans Laufen. Die Funktion "SHFileOperation" steht zwar in der API-Hilfe, wir von Delphi aber nicht akzeptiert.

MfG,
Peter


Klabautermann - Do 01.08.02 13:35

Hallo,

@Peter Lustig: denke an das "uses ShellAPI"

@GPF: Ich habe mir das komando noch mal in der Hilfe angesehen. Dort fand ich die angabe, das dieser Befehl unter Windows NT nicht zur verfügung steht. Ich habe ihn vor einer Weile mal unter W2k erfolgreich eingestzt. Weist du ab welcher Version er auch auf der NT schiene läuft?

Gruß
Klabautermann


Christian S. - Do 01.08.02 14:10

Zitat:

@Peter Lustig: denke an das "uses ShellAPI"

Ups! Damit funktioniert es jetzt.

In meiner Hilfe steht übrigens zu dem Befehl die Bemerkung "Now Supported on Windows NT".

MfG,
Peter


kradon - Do 01.08.02 15:20
Titel: Dummies
Hallo zusammen !!!

@Peter Lustig könntest du mir deinen Quellcode noch einmal per
Mail inklusive Kommentare zukommen lassen ? Sozusagen
zum selbststudium.
Bitte an: kradon@web.de

@GPF Würde mich freuen, wenn du das auch machen könntest

Mal eine Frage an alle, wie lange Programmiert ihr eigentlich schon und
mit welcher Version von Delphi ?

Ich für meinen teil habe vor 1 Jahr angefangen und programmiere
mit Delphi 3.

Gruß
Karsten


Christian S. - Do 01.08.02 15:51

@kradon:
Dein Studienobjekt ist abgeschickt, kommt von der Adresse:
arthur_philip_dent_42@gmx.de

Ich arbeite mit Delphi 6 Enterprise. Ich programmiere seit ca. 8 Jahren mit Pausen. Angefangen mit TP, dann ziemlich lange mit Delphi 3 und seit zwei oder drei Monaten mit Delphi 6. Habe aber so gut wie ausschließlich mit VCL gearbeitet. Das war ein Fehler, den ich langsam ändern will...

MfG,
Peter


GPF - Do 01.08.02 16:54

Ok, ich werde dann mal einen Kommentarversuch starten. Eine Kopie schicke ich Dir auch per Email zu.
Zuerst aber kurz zu Deinen Fragen:
Ich programmiere aktiv seit knapp 12 Jahren. Damals noch mit Basic und Assembler auf meinem alten C64.
1995 habe ich dann zum ersten Mal Delphi erblickt.
Eine Schulversion von Delphi 1 lag bei einem Buchhändler für 199,- DM in den Regalen. Da dachte ich noch, es wäre ein Multimedia Autoren System :shock: . Naja, dummerweise hatte ich dann kurze Zeit später auch das Geld zusammen und bin hingedackelt um mir die kleine Box zu kaufen. Nach ersten Annäherungsversuchen war dann aber der eigentliche Zweck dieser CD gelüftet und nach ca. einem halben Jahr war ich bereits voll von Delphi überzeugt. Später erschien dann Delphi 2, welches ich mir dann auch umgehend gekauft habe. Meine Programmierarbeit habe ich dann aber lange Zeit unter Delphi 1 weitergeführt, da ich Win3.1 Kompatibilität für einen Job brauchte. Irgendwann als klar war, daß alle Win3.1 Rechner aufgegeben worden sind, bin ich auf Delphi 5 Enterprise umgestiegen und seitdem damit voll zufrieden. D6 habe ich mir zwar auch bereits angesehen (PE) - allerdings lohnt sich noch kein Update - die Änderungen sind zu marginal.

So, nun aber zu den Programmerklärungen:

Der Code selber ist eigentlich nicht schwer zu verstehen. Der wichtigen Hauptfunktion SHFileOperationA wird ein Record TSHFileOpStructA übergeben, welche Quelle, Ziel, Parameter enthalten - und natürlich auch, was gemacht werden soll (kopieren, löschen, bewegen, umbenennen). Das A hinter SHFileOPStructA und SHFileOperationA bedeutet lediglich, das ich Ansi Strings benutze. Das A kann auch weggelassen werden, dann werden ganz normale 8 bit Zeichen verwendet - oder benutze W, für WideStrings.

TSHFileOpStructA ist dabei wie folgt deklariert:

TSHFileOpStructA = _SHFILEOPSTRUCTA;
_SHFILEOPSTRUCTA = packed record
Wnd: HWND;
wFunc: UINT;
pFrom: PAnsiChar;
pTo: PAnsiChar;
fFlags: FILEOP_FLAGS;
fAnyOperationsAborted: BOOL;
hNameMappings: Pointer;
lpszProgressTitle: PAnsiChar; { only used if FOF_SIMPLEPROGRESS }
end;

Die Parameter müssen je nach Operation entsprechend gefüllt werden. Genaues liest Du besser dazu in der Windows SDK Hilfe unter SHFILEOPSTRUCT nach. Hier aber einige Hinweise:

wFunc benennt die auszuführende Funktion: FO_COPY, FO_MOVE (wie hier verwendet), FO_RENAME; FO_DELETE.
Von diesem Parameter hängen im Prinzip alle weiteren Eingaben ab.
Wnd bezeichnet das Fensterhandle, falls der Fortschrittsdialog von Windows bei den Operationen angezeigt werden soll. Soll dieser Dialog nicht Modal angezeigt werden - oder eben gar nicht, dann setze hier 0 ein.
PFrom bezeichnet die Quelle der Operationen. Hier können beliebig viele Dateien angegeben werden. Unter Windows müssen die Dateien durch #0 voneinander getrennt werden. Ich habe mir die Hilfsfunktion ConvertFile geschrieben, welche Semikolon durch #0 ersetzt. Eine Liste von Dateien wird durch #0#0 abgeschlossen. Dies übernimmt diese Hilfsprozedur ebenfalls. Damit erkennt Windows das Ende einer Liste.
Hierbei sind auch Verzeichnisse und Wildcards erlaubt.
pTo ist das Ziel. Beim Löschen kann dieser Parameter natürlich leer bleiben. Üblicherweise ist dieses ein einziger Pfad. Sollen mehrere Zieldateien angegeben werden, so muß FOF_MULTIDESTFILES als Flag mit übergeben werden. Außerdem muß für jede Quelldatei (bzw. Pfad) auch ein Zielpfad angeben werden. In einem solchen Fall gelten die selben Regeln wie bei dem Parameter pFrom (also #0, Wildcards, etc).
fFlags ist ebenfalls ein wichtiger Parameter. Hier werden einige zusätzliche Details angegeben. Hier eine kurze Übersicht aller erlaubten Parameter:
FOF_ALLOWUNDO: Undo Angaben mitspeichern - damit kann man z.B. Dateiobjekte in den Papierkorb verschieben
FOF_FILESONLY: Nur Dateien verwenden - keine Ordner
FOF_MULTIDESTFILES: siehe pTo
FOF_NOCONFIRMATION: Unterbindet sämtliche Abfragen und beantwortet diese mit "Immer" - wenn z.B. eine Zieldatei bereits existiert wird diese ohne Abfrage überschrieben
FOF_NOCONFIRMMKDIR: Verzeichnisse werden ohne Abfrage des Users angelegt
FOF_RENAMEONCOLLISION: Falls Zieldateien bereits existieren werden die Dateien umbenannt
FOF_SILENT: Unterbindet den Windows eigenen Fortschrittsdialog.
FOF_SIMPLEPROGRESS: Zeigt nur den Fortschrittsbalken an - aber keine Dateinamen
FOF_WANTMAPPINGHANDLE: Erstellt im Parameter hNameMappings ein Log aller Operationen mit Quell und Zieldatei. Ist dieser Parameter gesetzt, so muß hNameMapping vom Programmierer manuell wieder durch SHFreeNameMappings freigegeben werden.
fAnyOperationsAborted sollte selbsterklärend sein. Ist True falls eine Operation nicht erfolgreich durchgeführt worden ist. Berücksichtige, daß wenn Operationen nicht erfolgreich waren der Ursprungszustand NICHT wieder hergestellt wird!
lpszProgressTitle kannst Du setzen, wenn Du einen einfachen Fortschrittsbalken anzeigen lassen möchtest. Mit diesem Parameter kannst Du den Titel setzen. In diesem Fall muß allerdings FOF_SIMPLEPROGRESS gesetzt sein

Berücksichtige, daß Du nur PChars verwenden darfst, wenn Stringwerte angibst!

Das ist eine kurze Erklärung zu der Struktur TSHFileOPStructA.
Zu meinem Code:
Die Funktion ConvertFile ersetzt alle Semikolon durch #0 und gibt einen PChar zurück.

In der Verschiebeprozedur fülle ich einfach die Struktur mit den übergebenen und benötigten Verschiebe-Parametern. Ich habe mich entschieden, daß ich auch die Flags als Parameter der Hauptfunktion übergebe, da ich in meinen Programmen ab und zu einen Verschiebe-Dialog anzeigen lassen möchte.
Nicht benutze Werte setze ich auf nil (hNameMappings, lpszProgressTitle).
SHFileOperation ist selber auch eine Funktion und liefert 0 zurück wenn die Dateioperation erfolgreich ausgeführt worden ist.
Das ist eigentlich schon das gesamte Geheimnis.

Um nun Dateien zu kopieren mußt Du nur wFunc auf FO_COPY setzen - der Rest kann bleiben.
Möchtest Du Dateien Löschen, so setzte wFunc auf FO_DELETE und pTo auf nil. Natürlich solltest Du bei beidem mit den Flags spielen um das gewünschte Resultat zu erzielen.


kradon - Fr 02.08.02 10:11
Titel: Vielen Dank
So habe mir die Unterlagen gerade mal angeschaut
und werde sie nun durcharbeiten.

Damit wird sich dann warscheinlich meine Delphikenntnisse
verdoppel. :lol:

Gruß
Karsten