Autor |
Beitrag |
Bääääär
      
Beiträge: 117
|
Verfasst: Di 24.10.06 19:10
Ich weiß, es nervt euch sicher alle, aber ich möchte alle Dateien eines Ordners kopieren, und zwar ohne API und mit Fortschrittsbalken. Ich habe mir dazu eine kleine Routine gebastelt, die aber irgendwie nich funtionieren will.
Variablen:
textlabel:Tlabel, darauf soll angezeigt werden, welche Datei grade kopiert wird.
progresslabel:TLabel, es soll den Fortschritt in Prozent anzeigen.
Progress:Tprogressbar, zeigt den Fortschritt an,
items:Tstrings, beinhaltet Ordner und Dateinamen, die kopiert werden sollen.
Topath:String, der Zielpfad.
Die Function findtopath soll den Zielpfad zum Kopieren herausfinden. Dazu wird der ursprungspfad genommen, und ein Teil davon abgezogen (nämlich genau den angegebenen Ordner in "items", z.B. "C:\ordner\"). Anschließend wird der Zielordner dazugefügt. Befand sich die Datei in "C:\ordner\ordner2\hallo.txt" und in "items" stand "C:\ordner\" und der Zielordner ist "C:\", dann soll die Datei in "C:\ordner2\hallo.txt" landen (ich hoffe, das war jetzt verständlich... ^^)
Die Function "VerzGroesse" soll die Größe aller Dateien im in "items" angegebenen Ordner herausfinden, damit zu begin die gesammt zu kopierende Menge an Daten herausgefunden werden kann (für den Fortschrittsbalken).
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: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203:
| unit copyunit;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, XPMan, StdCtrls, Menus, appevnts,ShellApi, ComCtrls, inifiles, FileCtrl;
Function VerzGroesse(Verzeichnis:string):longint; procedure startsave(items:TStrings; fileprogress,progress:Tprogressbar; textlabel,filelabel,progresslabel:TLabel; Topath:String); var gefunden: TStrings; VerzListe:Tstringlist; groesse:longint; mem:Tmemorystream;
implementation
procedure GetFilesInDirectory(Directory: String; const Mask: String; List: TStrings; WithSubDirs, ClearList: Boolean); procedure ScanDir(const Directory: String); var SR: TSearchRec; begin if FindFirst(Directory + Mask, faAnyFile - faDirectory, SR) = 0 then try repeat List.Add(Directory + SR.Name) until FindNext(SR) <> 0; finally FindClose(SR); end;
if WithSubDirs then begin if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try repeat if ((SR.attr and faDirectory) = faDirectory) and (SR.Name <> '.') and (SR.Name <> '..') then ScanDir(Directory + SR.Name + '\'); until FindNext(SR) <> 0; finally FindClose(SR); end; end; end;
begin List.BeginUpdate; try if ClearList then List.Clear; if Directory = '' then Exit; if Directory[Length(Directory)] <> '\' then Directory := Directory + '\'; ScanDir(Directory); finally List.EndUpdate; end; end;
Function VerzGroesse(Verzeichnis:string):longint; Var SR: TSearchRec; Groesse: longint; Begin Groesse:=0; If Verzeichnis[length(Verzeichnis)]<>'\' Then Verzeichnis:=Verzeichnis+'\'; If FindFirst(Verzeichnis+'*.*',$3F,SR)=0 Then Begin inc(files); If ((SR.Attr and faDirectory)>0) and (SR.Name<>'.') and (SR.Name<>'..') Then Begin Groesse:=Groesse+VerzGroesse(Verzeichnis+SR.Name) End Else Begin Groesse:=Groesse+SR.Size; End; If (SR.Name<>'.') and (SR.Name<>'..') Then VerzListe.Add(Verzeichnis+SR.Name); While FindNext(SR)=0 do Begin inc(files); If ((SR.Attr and faDirectory)>0) and (SR.Name<>'.') and (SR.Name<>'..') Then Begin Groesse:=Groesse+VerzGroesse(Verzeichnis+SR.Name) End Else Begin Groesse:=Groesse+SR.Size; End; If (SR.Name<>'.') and (SR.Name<>'..') Then VerzListe.Add(Verzeichnis+SR.Name); End; End; FindClose(SR); Result:=Groesse; End;
function GetFileSizeEx(const AFileName: String): Int64; var F: TSearchRec; begin Result := -1; if FindFirst(AFileName, faAnyFile, F) = 0 then begin try Result := F.FindData.nFileSizeLow or (F.FindData.nFileSizeHigh shl 32); finally SysUtils.FindClose(F); end; end; end;
procedure copy(FromPath,ToPath:string); begin mem.LoadFromFile(frompath); mem.SaveToFile(topath); end;
function findtopath(todir,filename,entf:String):string; var i:integer; s:string; begin for i:=0 to length(entf)-1 do begin s:=filename; s[1]:=char(#0); end; result:=includetrailingbackslash(todir)+s; end;
function givefile(filename:string):string;
var laufwerk: char; verzeichnisse: string; begin ProcessPath(filename, laufwerk, verzeichnisse, result); end;
procedure startsave(items:TStrings; fileprogress,progress:Tprogressbar; textlabel,filelabel,progresslabel:TLabel; Topath:String); var i,j, copiedfiles:longint; s:string; begin
textlabel.Caption:='System Save bereitet den Kopiervorgang vor ...';
groesse:=0; if items.count>100 then begin showmessage('Es dürfen nicht mehr als 100 Verzeichnisse und Dateien angegeben werden!'); exit; end; for i:=0 to items.Count-1 do begin s:=items[i]; if s[length(s[i])]<>'\' then begin try groesse:=groesse+getfilesizeex(s); finally end; end else groesse:=groesse+verzgroesse(items[i]); end;
fileprogress.Min:=0; progress.Min:=0; fileprogress.max:=100; progress.Max:=100; fileprogress.Position:=0; progress.position:=0; textlabel.Caption:='Bereitet den Kopiervorgang vor ...'; mem:=tMemorystream.Create; copiedfiles:=0;
for j:=0 to items.Count-1 do begin if extractfileext(items[j])='' then begin GetFilesInDirectory(items[j], '*.*',gefunden, true, true); for i:=0 to gefunden.Count-1 do begin if (extractfileext(gefunden[i])<>'')and (fileexists(gefunden[i])) then /wenn die Datei wirklich eine Datei ist, und auch existiert, dann ... begin inc(copiedfiles,getfilesizeex(gefunden[i])); progresslabel.caption:=inttostr((copiedfiles div groesse)*100)+'%'; textlabel.caption:=gefunden[i]+findtopath(topath,gefunden[i],items[j]); if fileexists(gefunden[i]) then copy(gefunden[i],findtopath(topath,gefunden[i],items[j])); end; end; end else inc(copiedfiles,getfilesizeex(items[j])); progresslabel.caption:=inttostr((copiedfiles div groesse)*100)+'%'; textlabel.caption:=gefunden[i]+findtopath(topath,gefunden[i],items[j]); if fileexists(items[j]) then copy(items[j],findtopath(topath,items[j],givefile(items[j]))); end;
mem.Free;
end;
end. |
So, ich hoffe, das war verständlich...
Danke schon mal für die Mühe, das alles durchzulesen, und in erwartung auf eure Antworten
The Bääääär
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 24.10.06 20:08
Warum denn nicht einfach mit der API??? Ich meine, das heißt doch nicht, dass der Windows Kopierdialog angezeigt werden muss!
Jedenfalls dürfte deine eine der langsamsten und speicheríntensivsten Lösungen sein, die mir zu dem Thema einfallen würden...
Jedenfalls: Wie Luckie  und ein paar andere im Forum (mich eingeschlossen) immer wieder bräuchte ich jetzt dringend eine Glaskugel...
WAS funktioniert denn nicht???
Steht da, wenn du das Programm startest da
Bääääär hat folgendes geschrieben: | irgendwie nich funtionieren will. |
als Fehlermeldung oder was passiert sonst?
Und wie stehts mit Debugging? Wird die Dateiliste nicht korrekt gefunden, nicht korrekt abgearbeitet, oder was?
//EDIT: Ich überfliege den Code trotzdem mal, aber ob ich da was sehe, wenn ich nicht weiß, was nicht geht, naja, mal sehen...
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Di 24.10.06 23:35
Sorry!
Er meint, es wäre eine Zugriffsverletzung. Aber die Funktion für die Liste der Dateien (Name: "gefunden") funzt, das habe ich in einem Extra-Programm getestet. Alle Funktionen laufen einzeln, aber zusammen woll'n sie nich. Wie gesagt, Zugriffsverletzung...
Danke trotzdem,
The Bääääär
|
|
Lannes
      
Beiträge: 2352
Erhaltene Danke: 4
Win XP, 95, 3.11, IE6
D3 Prof, D4 Standard, D2005 PE, TurboDelphi, Lazarus, D2010
|
Verfasst: Di 24.10.06 23:49
Hallo,
abgesehen davon das man TStrings nicht direkt verwenden sollte,
fehlen TStrings.Create bzw. TStringList.Create in deinem Code.
_________________ MfG Lannes
(Nichts ist nicht Nichts) and ('' <> nil ) and (Pointer('') = nil ) and (@('') <> nil )
|
|
AXMD
      
Beiträge: 4006
Erhaltene Danke: 7
Windows 10 64 bit
C# (Visual Studio 2019 Express)
|
Verfasst: Mi 25.10.06 00:18
@Bäääääär: du willst keine API-Funktionen verwenden? Was meinst du was FindFirst, FindNext etc. sind? Gekapselte API-Funktionen...
AXMD
|
|
C.Schoch
      
Beiträge: 28
WinXp
Turbo Dephi Win 32
|
Verfasst: Mi 25.10.06 00:18
Hi,
Wohl dem der viel Arbeitsspeicher hat!
Delphi-Quelltext 1: 2: 3: 4: 5:
| procedure copy(FromPath,ToPath:string); begin mem.LoadFromFile(frompath); mem.SaveToFile(topath); end; |
Läuft das ganze in einem Thread? wenn Ja wie übergiebst du die Variablen?
Kann es sein, dass die Dateien gesperrt sind die Du kopieren möchtests.
_________________ Das System hofft auf Besserung
[Siemens]
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Mi 25.10.06 23:27
Hey, nehmt's mir nicht übel! Ich wollte einfach mal selber soein Programm schreiben. Ok, mit dem "Ohne API" meinte ich, dass man mir nicht wieder diesen Anderen Vorschlag vorschreiben soll, auf den hier so oft verwiesen wird (weiß grade nicht, wie der hieß, egal)
@C.Schoch: Nee, es läuft nich in 'nem Thread, warum?
@AXMD: Mmhh. Das wusste ich nicht...
@alle: Klar, meine Variante ist vielleicht nicht die beste und sicher nicht die "speicherunintensivste" aber darum ging es mir auch gar nicht.
So, ich habe jetzt Delphi-Quelltext 1: 2: 3: 4:
| gefunden:=TStrings.create; ... ... gefunden.free; | hinzugefügt. Jetzt kommt an der gleichen Stelle, wo vorher der Fehler war, ein neuer Fehler 'Abstakter Fehler'. Für mich nicht sehr aussagekräfitg. Kann mir da vielleicht jemand helfen?
|
|
AXMD
      
Beiträge: 4006
Erhaltene Danke: 7
Windows 10 64 bit
C# (Visual Studio 2019 Express)
|
Verfasst: Mi 25.10.06 23:33
TStrings ist abstrakt. Von einer abstrakten Klasse kannst du keine Instanz erstellen. Nimm eine der Kindklassen (z.B. TStringList)
AXMD
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 25.10.06 23:34
Tja, weil du TStrings.Create statt TStringList.Create benutzt.
In TStrings sind einige Sachen nur definert "wie die Funktionen aussehen" aber nicht implementiert bzw. die ganze Klasse ist nur definiert aber beinhaltet keinen Code dazu. Der entsprechende Code wird dann erst in einer davon abgeleiteten Klasse geschrieben.
So kannst du dann, egal um welche abgeleitete Klasse es sich handelt, sagen: Rufe die Funktion xy auf, die in der Oberklasse definiert wurde. Wie diese dann in der abgeleiteten Klasse implemetiert ist, ist dann egal.
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 00:38
Ah, so ist das also! ^^ Ok, jetzt geht's, leider läuft die Sache mit dem Findtopath noch nicht so ganz. Naja, das werde ich auch noch schaffen. Danke jaenicke!
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 26.10.06 00:52
Naja, dazu noch ein Tipp: Mit Delete löschst du was aus einem String. Und wenn du jetzt "c:\ordner" aus "c:\ordner\xyz" löschen willst, dann schreib einfach Delete('c:\ordner\xyz', 1, Length('c:\ordner')). Dann werden am Anfang so viele Zeichen gelöscht wie der zweite String welche hat. Im Beispiel würde also "\xyz" bei herauskommen. (Es geht allerdings nicht genauso, denn Delete muss als ersten Parameter eine echte Variable bekommen, aber das soll ja auch nur ein Beispiel zur Veranschaulichung sein...
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 01:28
Danke, aber ich hab's jetzt anders gelöst. Der Zielpfad ergibt sich aus dem Topath + den ursprungsname der Datei ohne Laufwerk. Also Aus C:\zielordner\ und C:\ursprungsordner\datei.dat wird C:\zielordner\ursprungsordner\datei.dat.
Und ich habe noch ein Forcedirectories vor doe Kopieren-Zeile gesetzt. ^^ das war nämlich schon wieder der nächste Fehler ('das Verzeichnis existiert nicht ...'). ^^
Aber nun geht das kopieren! Nur der Fortschrittsbalken will noch nicht so ganz. Ich habe festgestellt, dass irgendwas mit dem Zusammenrechnen der Gesammtdatenmenge nicht stimmt. die Menge der kopierten Daten ist nämlch zum schull größer als die geesammten Daten...
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 08:56
So, der Fehler beim berechnen der Datenmenge ist behoben: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| groesse:=0; if items.count>100 then begin showmessage('Es dürfen nicht mehr als 100 Verzeichnisse und Dateien angegeben werden!'); exit; end; for i:=0 to items.Count-1 do begin s:=items[i]; if s[length(s)]<>'\' then begin try groesse:=groesse+GetFileSizeEx(s); finally end; end else groesse:=groesse+verzgroesse(s); end; application.ProcessMessages; |
Auch beim Errechnen des Fortschritts gibt's noch Probleme. copiedfiles ist zum schluss exakt gleich Groesse, aber der Fortschrittsbalken, der bei mir jetzt durch progress.Position:=round((copiedfiles/groesse)*100); berechnet wird fängt irgendwie bei 50% an, endet jedoch am Ende genau bei 100%. So progress.Position:=(round((copiedfiles/groesse)*100)-50)*2; klappts dann einwandfrei. Jedoch scheint mir die Rechnung etwas absurd...
Dann noch eine Frage: Wie kann man soetwas denn beschleunigen, weil ihr ja alle meintet, meine Variante wäre extrem langsam.
The bääääär
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 26.10.06 09:09
In dem du API-Funktionen benutzt. Entweder CopyFile oder CopyFileEx, wenn du einen Kopierfortschritt haben willst. Desweiteren lässt sich bei SHFileOperation auch der Dialog unterdrücken.
BTW ohne API-Funktionen wird es nie gehen, wie soll sich denn dein Programm mit Windows verständigne, wenn nicht über die WinAPI? Selbst hinter den Stream stehen nur API-Funktionen.
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 09:17
Ok, wie gesagt, ich meinte dieses FileShareOperation. Kannst du mir sage, wie ich mit diesem CopyFileEx umzugehen habe?
Du meinst ich soll es einfach anstelle meiner memorystream-kopier-procedure einsetzen?
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 26.10.06 09:19
Eine Suche mit Google oder im Forum hätte es auch getan:
www.michael-puff.de/...iges/CopyFileEx.html
Warum nimmst du die überhaupt? Und was soll das, dass du keine API-Funktionen nehmen willst?
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 09:24
Weil überall, wo man fragt auf dieses SHFileOperation hingewiesen wird. Ich will aber nicht, dass dieses dämliche Fenster erscheint und ich will auch nicht, dass bei einer beschädigten Datei gleich der gane kopiervorgang abgebrochen wird. Das war ja für mich der einzige grund, das ganze selber zu schreiben.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 26.10.06 09:51
Was für ein dämliches Fenster? Die GUI ist doch nur optional, wenn du SHFileOperation benutzt! Ich behaupte mal, du hast dir die Funktion noch nie angesehen. Du kannst da natürlich angeben, ob Fehler angezeigt werden sollen, ob Bestätigungen vom User eingeholt werden sollen, ob eine Fortschirttsanzeige kommen soll oder nicht, etc.
Sieh dir die Funktion doch erstmal an, bevor du sagst: "Nutze ich nicht"
msdn.microsoft.com/l...s/shfileopstruct.asp
Was die Fehlertoleranz angeht: Da hab ich im Moment keine Ahnung, was bei einem Fehler passiert, wenn die Fehleranzeige abgeschaltet ist. Ich nehme an, der bricht trotzdem ab?
Aber da kann dir Luckie vermutlich mehr zu sagen.
|
|
Bääääär 
      
Beiträge: 117
|
Verfasst: Do 26.10.06 17:05
Natürlich habe ich sie mir angeschaut, warum sonst sagen alle, an soll diese Function verwenden. Aber ich verstehe das ganze Ding nicht, ich weiß nicht, wie ich das verwenden kann. Und für mich sah das so aus, als könnte man den Fortschritt nur mit diesem Info-Fenster von Win anzeigen. Und das wollte ich nicht.
|
|
Sinspin
      
Beiträge: 1335
Erhaltene Danke: 118
Win 10
RIO, CE, Lazarus
|
Verfasst: Do 26.10.06 18:28
mal eine ganz andere richtung:
ich habe mir schon ettliche male richtig fehlerresistente kopierroutinen gebastelt. und zwar nicht mit direkten win API aufrufen.
sondern mit dem delphi kapselungen AssignFile, Reset, ReWrite, BlockRead, BlockWrite und CloseFile. über die FileMode variable kamm man auch schön bestimmen wie man die dateien auf machen will.
Ich habe damit immer sehr gute erfahrungen gemacht. braucht wenig speichern und ist ordentlich schnell wenn man eine angemessende puffergröße verwendet.
_________________ Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
|
|
|