Dateien und Verzeichnisse kopieren

Für allgemeine Fragen zur Programmierung mit PureBasic.
Robert
Beiträge: 25
Registriert: 14.09.2004 23:24

Dateien und Verzeichnisse kopieren

Beitrag von Robert »

Hallo,

ich hab ein Problem mit dem Callback von CopyFileEx_. Das Ding loopt. Hier ein Beispiel:

Code: Alles auswählen

#COPY_FILE_FAIL_IF_EXISTS = 1
#COPY_FILE_RESTARTABLE = 1

Procedure.l CopyProgressRoutine(TotalFileSize.l, TotalBytesTransferred.l, StreamSize.l, StreamBytesTransferred.l, dwStreamNumber.l, dwCallbackReason.l, hSourceFile.l, hDestinationFile.l, lpData.l)

  debug TotalFileSize
  debug TotalBytesTransferred
  debug StreamSize
  debug StreamBytesTransferred

  ProcedureReturn 0

EndProcedure


DeleteFile("c:\test\y")
DeleteFile("c:\test\z")

Result = CopyFileEx_("c:\test\x", "c:\test\y", @CopyProgressRoutine(), 0, 0, #COPY_FILE_FAIL_IF_EXISTS | #COPY_FILE_RESTARTABLE)
Result = CopyFileEx_("c:\test\x", "c:\test\z", @CopyProgressRoutine(), 0, 0, #COPY_FILE_FAIL_IF_EXISTS | #COPY_FILE_RESTARTABLE)

debug Result

MessageRequester("Copy", "Dateien wurden erfolgreich kopiert!", #MB_ICONINFORMATION)

end
Wenn ich aber die beiden letzten Parameter (hDestinationFile.l, lpData.l) weglasse, funktioniert das ganze. Allerdings gibt das Callback keine Werte für transferred Bytes zurück. Hab ich da irgendwo ein Fehler drin oder was ist da los?

Eigentlich würd ich ja viel viel lieber die PB-Befehle CopyFile + CopyDirectory verwenden. Hier weiß ich allerdings nicht, wie man den Fortschritt für eine Progressbar ermitteln kann. Hat da vielleicht jemand eine Idee/Lösung???
sbehrens
Beiträge: 274
Registriert: 08.09.2004 18:41
Kontaktdaten:

Beitrag von sbehrens »

Eigentlich würd ich ja viel viel lieber die PB-Befehle CopyFile + CopyDirectory verwenden. Hier weiß ich allerdings nicht, wie man den Fortschritt für eine Progressbar ermitteln kann. Hat da vielleicht jemand eine Idee/Lösung???
Ich würde das so machen, dass am Anfang erst die Größe aller Dateien *mal*(also "*" bzw: Multiplikation) die Anzahl der Dateien in einer Variable gespeichert wird (zB. Copy_All). Dann wird beim kopieren einer Datei die Größe dieser (Datei) zu zB. Copy_AlreadyDone hinzugefügt. Wenn man dann Copy_AlreadyDone durch Copy_All teilt (zB. kann 0.571 rauskommen), muss man die Zahl nur noch *100 nehmen, und man hat den Fortschritt in % errechnet. (zB. 57.1%) Es kann sein, dass ich das jetzt sehr unverständlich erklärt habe, aber ich hoffe, dass du verstehst was ich meine. (Ich weiß nicht ob diese Methode sehr effektive ist.) Ich kann dir Morgen vielleicht ein Beispielcode schreiben, aber jetzt bin ich zu müde.
Kurz:
Copy_All = Anzahl der Datein * Größe aller Dateien zusammen
Copy_AlreadyDone = Copy_AlreadyDone + Dateigröße
Copy_PercentDone = (Copy_AlreadyDone / Copy_All) * 100

mfG
Basti

//Edit: Ok, schade dass ich dir nicht helfen konnte, an der Formel ist sowieso was falsch! (Copy_All müsste nur die Größer der Dateien enthalten, wie gesagt; Ich war sehr müde!)
Zuletzt geändert von sbehrens am 16.09.2004 16:30, insgesamt 1-mal geändert.
enjoy life... while you can!
Robert
Beiträge: 25
Registriert: 14.09.2004 23:24

Beitrag von Robert »

Danke erstmal an ptI.

Ich glaube, ich hätte mich klarer ausdrücken sollen. Das Problem ist nicht die Formel für die Progressbar. Die hab ich bereits, sowohl für den Kopier-Fortschritt von einer Datei, als auch für den Gesamtfortschritt für alle zu kopierenden Dateien.

Das Problem ist vielmehr, daß ich nicht weiß, woher ich die Summe der bereits transferrierten Bytes (die für die Formel benötigt werden) herbekommen soll, wenn ich den PB-Befehl CopyFile/CopyDirectory benutze. Die Progressbar soll natürlich während des Kopiervorganges ständig aktualisiert werden. Wie löst man so etwas ???

Es gibt doch bestimmt eine Lösung/Lösungsansatz?! Oder?
Robert
Benutzeravatar
bobobo
jaAdmin
Beiträge: 3873
Registriert: 13.09.2004 17:48
Kontaktdaten:

Beitrag von bobobo »

in etwa so vielleicht
mein c:\test\x ist ca 27,5MB groß und die Verhältnisse in der progressbar sind FIX!

Code: Alles auswählen

Enumeration
  #Window_0
  #ProgressBar_0
  #Button_0
EndEnumeration

Global i

#COPY_FILE_FAIL_IF_EXISTS = 1 
#COPY_FILE_RESTARTABLE = 1 

  If OpenWindow(#Window_0, 216, 0, 600, 300,  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar , "New window ( 0 )")
    If CreateGadgetList(WindowID())
      ProgressBarGadget(#ProgressBar_0, 0, 190, 400, 30, 0,2887,#PB_ProgressBar_Smooth)
      ButtonGadget(#Button_0, 50, 60, 140, 30, "")
    EndIf
  EndIf

Procedure.l CopyProgressRoutine(TotalFileSize.l, TotalBytesTransferred.l, StreamSize.l, StreamBytesTransferred.l, dwStreamNumber.l, dwCallbackReason.l, hSourceFile.l, hDestinationFile.l, lpData.l) 

  Debug "TotalFileSize "+Str(TotalFileSize )
  ;Debug "TotalBytesTransferred"+Str(TotalBytesTransferred )
  Debug "StreamSize    "+Str(StreamSize )
  ;Debug "StreamBytesTransferred"+Str(StreamBytesTransferred )
  SetGadgetState(#Progressbar_0,StreamSize/10000)
EndProcedure 

DeleteFile("c:\test\y") 
DeleteFile("c:\test\z") 

Repeat
  Event = WaitWindowEvent()
  If Event = #PB_EventGadget
    ;Debug "WindowID: " + Str(EventWindowID())
    GadgetID = EventGadgetID()
    If GadgetID = #ProgressBar_0
      Debug "GadgetID: #ProgressBar_0"
    ElseIf GadgetID = #Button_0
      Result = CopyFileEx_("c:\test\x", "c:\test\y", @CopyProgressRoutine(), 0, 0, #COPY_FILE_FAIL_IF_EXISTS | #COPY_FILE_RESTARTABLE) 
      Result = CopyFileEx_("c:\test\x", "c:\test\z", @CopyProgressRoutine(), 0, 0, #COPY_FILE_FAIL_IF_EXISTS | #COPY_FILE_RESTARTABLE) 
      Debug Result 
      MessageRequester("Copy", "Dateien wurden erfolgreich kopiert!", #MB_ICONINFORMATION) 
      End
    EndIf
  EndIf
Until Event = #PB_EventCloseWindow
End
ich hab hier XP übrigens
‮pb aktuel 6.2 windoof aktuell und sowas von 10
Ich hab Tinnitus im Auge. Ich seh nur Pfeifen.
Robert
Beiträge: 25
Registriert: 14.09.2004 23:24

Beitrag von Robert »

2bobobo

ich hab hier die XP Home Edition SP2 und bei mir wird immer nur die erste Datei kopiert, dann bleibt das Ding einfach stehen. Das hab ich jetzt mehrmals mit PB 3.91 und auch mit PB 3.81 getestet. Werden bei dir tatsächlich die beiden Dateien y und z erstellt?
Robert
Benutzeravatar
bobobo
jaAdmin
Beiträge: 3873
Registriert: 13.09.2004 17:48
Kontaktdaten:

Beitrag von bobobo »

y wird erstellt ..z wohl nicht

am ende der copyroutine scheint das Programm stehen zu bleiben ..
‮pb aktuel 6.2 windoof aktuell und sowas von 10
Ich hab Tinnitus im Auge. Ich seh nur Pfeifen.
Robert
Beiträge: 25
Registriert: 14.09.2004 23:24

Beitrag von Robert »

2bobobo

genau - und wenn man die letzten beiden Parameter löscht funktioniert das ganze. Ich glaub, das hat was mit der Länge der Parameter zu tun, denn die ersten vier sind sogenannte ULONG's.

Hier ein Auszug von VBnet:

CopyProgressRoutine Callback members

* TotalFileSize - ULONG - total file size, in bytes
* TotalBytesTransferred - ULONG - total number of bytes transferred
* StreamSize - ULONG - total number of bytes for this stream
* StreamBytesTransferred - ULONG - total number of bytes transferred for this stream
* dwStreamNumber - LONG - the current stream
* dwCallbackReason - LONG - reason for callback
* hSourceFile - LONG - handle to the source file
* hDestinationFile - LONG - handle to the destination file
* lpData - LONG - passed by CopyFileEx

Im engl. PB-Forum hab ich was gelesen, daß in PB 4.0 in der Richtung was unternommen wird. Na ja, die letzten beiden Parameter brauch ich ja nun wirklich nicht unbedingt ...

Danke für deine Hilfsbereitschaft!
Robert
Andreas
ToolbarKönig
Beiträge: 142
Registriert: 08.09.2004 08:33

Beitrag von Andreas »

@Robert,

wie wäre es mit SHFileOperation ?
Damit bekommst Du die Dialoge von Windows angezeigt. Mit Fortschrittsanzeige, Warnmeldungen usw.

Code: Alles auswählen

Enumeration 
  #Window_0 
  #Button_0 
EndEnumeration 

Procedure CopyFileWithProgress(Parent.l,In$,Out$,Trenner$)
;Trennzeichen in In$ durch Nullbyte ersetzen
Trenner.b = Asc(Trenner$)
Length.l = Len(In$)
For Pos.l = 0 To Length - 1
  ASCII.b = PeekB(@In$ + Pos)
  If ASCII <> Trenner
  PokeB(@In$ + Pos, ASCII)
  Else
  PokeB(@In$ + Pos, 0)
  EndIf
Next
;Trennzeichen in Out$ durch Nullbyte ersetzen
Length.l = Len(Out$)
For Pos.l = 0 To Length - 1
  ASCII.b = PeekB(@Out$ + Pos)
  If ASCII <> Trenner
  PokeB(@Out$ + Pos, ASCII)
  Else
  PokeB(@Out$ + Pos, 0)
  EndIf
Next
SHFO.SHFILEOPSTRUCT
SHFO\hwnd   = Parent
SHFO\wFunc  = #FO_COPY 
SHFO\pFrom  = @In$
SHFO\pTo    = @Out$
SHFO\fFlags = #FOF_MULTIDESTFILES 
If SHFileOperation_(SHFO) = 0
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndProcedure


If OpenWindow(#Window_0, 216, 0, 600, 300,  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar , "New window ( 0 )") 
If CreateGadgetList(WindowID()) 
  ButtonGadget(#Button_0, 50, 60, 140, 30, "Copy") 
EndIf 
Repeat 
  Event = WaitWindowEvent() 
  If Event = #PB_EventGadget 
    GadgetID = EventGadgetID() 
    If GadgetID = #Button_0
      ;sollen mehrere Files kopiert werden, muessen die einzelnen Files durch
      ;ein Nullbyte abgechlossen werden, der letzte File durch ein doppeltes Nullbyte.
      ;Da das in der Prozedur geschieht, wird hier ein anderes Trennzeichen(|) benutzt,
      ;und an die Prozedur uebergeben.
      Sourcefiles$="m:\test\x|m:\test\x||";PFADE ANPASSEN !!
      DestFiles$="m:\test\z|m:\test\y||";PFADE ANPASSEN !!
      If CopyFileWithProgress(WindowID(),SourceFiles$,DestFiles$,"|")  
         MessageRequester("Copy", "Dateien wurden erfolgreich kopiert!", #MB_ICONINFORMATION) 
      Else
         MessageRequester("Copy", "Dateien wurden nicht kopiert !", #MB_ICONINFORMATION) 
      EndIf
    EndIf 
  EndIf 
Until Event = #PB_EventCloseWindow 
End 
EndIf 
Gruss Andreas
Robert
Beiträge: 25
Registriert: 14.09.2004 23:24

Beitrag von Robert »

Hallo Andreas,

sorry daß ich mich erst jetzt melde, hab gestern keine Zeit mehr gehabt zu antworten. Dein Programm funktioniert natürlich prächtig.
Das Problem ist nur, daß ich die Dateien/Verzeichnisse nicht alle auf einmal, sondern nur einzeln an SHFileOperation übergeben kann. Somit erhalte ich zwar für jede Datei eine Fortschrittsanzeige, eine Gesamtfortschrittsanzeige ist jedoch nicht möglich. Und auf den Komfort will man natürlich nicht verzichten. Der ideale Befehl hierfür scheint wirklich das CopyFileEx mit dem Callback zu sein. Denn da erhält man pro Datei die übertragenen Bytes, kann diese hochaddieren und einen Einzel- und Gesamtfortschritt darstellen.

Deine Lösung hab ich mir aber trotzdem gleich auf die Seite getan, da ich noch vorhabe, mir irgendwann mal ein kleines Backup-Programm zu schreiben. Und für solche Art von Kopierprogrammen ist es ideal.

Danke sehr.
Robert
nalor
Beiträge: 10
Registriert: 29.06.2009 15:48

Beitrag von nalor »

Falls nochmal jemand über dieses Thema stolpert - in PureBasic gibts mittlerweile den Variablentyp Quad und der entspricht wohl einem ULONG - hier kurz die Funktion wie sie bei mir dann korrekt funktioniert (aber alles ohne Gewähr):

Code: Alles auswählen

#PROGRESS_CONTINUE=0
#PROGRESS_CANCEL=1
#PROGRESS_STOP=2
#PROGRESS_QUIET=3

Procedure.l CopyProgressRoutine(TotalFileSize.q, TotalBytesTransferred.q, StreamSize.q, StreamBytesTransferred.q, dwStreamNumber.l, dwCallbackReason.l, hSourceFile.l, hDestinationFile.l, lpData.l)

;	Debug "TotalFileSize "+Str(TotalFileSize )
;	Debug "TotalBytesTransferred"+Str(TotalBytesTransferred )
;	Debug "StreamSize    "+Str(StreamSize )
;	Debug "StreamBytesTransferred"+Str(StreamBytesTransferred )
	Debug "Progress: "+Str(TotalBytesTransferred/(TotalFileSize/100))
  
	ProcedureReturn(#PROGRESS_CONTINUE)  
EndProcedure
Hoffe es hilft jemandem :)
Antworten