Angeregt durch eine Anfrage im Forum habe ich mal eine FileCopy-Funktion mit GUI erstellt, die alle Infos des Window-Copy vereint: FileCopyProgress
Aber Bilder sagen mehr als Worte:
Wie mit FileCopy lassen sich einzelne Dateien oder Ordner kopieren. Die Verwendung von Wildcards ist möglich. Die Syntax ist also an diese Funktion angepasst.
Zusätzlich können Ausschluss-Attribute übergeben werden, standardmäßig sind das "SH".
Syntax: _FileCopyProgress($_sSourcePath, $_sDestinationFolder, $_vFlag, $_sAttribExclude)
Bei einem Fehler wird dieser angezeigt bzw. mit $FC_LOGCOPY in das Log geschrieben. Der Kopiervorgang für evtl. noch zu kopierende Dateien wird danach fortgesetzt.
Die Copy-Flags sind identisch zur FileCopy-Funktion. Aber es gibt auch zwei neue!
$FC_LOGCOPY, erstellt eine Datei "TARGET\FOLDER\progresscopy.log", überschreibt evtl. vorhandene ältere Logdateien
$FC_OPENLOG, ! setzt automatisch das $FC_LOGCOPY Flag ! - Öffnet die Logdatei (wenn erstellt) nach Beendigung des Kopiervorganges
[2022-09-02 09:51:31] Start Copy "C:\VM_SHARE\*.GDB" --> "C:\CODE\Test"
Anzahl Dateien: 5
Gesamtgröße: 6.0 GB
[SUCCESS] C:\CODE\Test\GDI.GDB 1.2 GB
[SUCCESS] C:\CODE\Test\GDI_ORIG_2021_12_08.GDB 1.2 GB
[ERROR] C:\CODE\Test\GDI_ORIG_2022_01_10.GDB Die Datei ist bereits vorhanden.
[SUCCESS] C:\CODE\Test\GDI_ORIG_2022_02_28.GDB 1.2 GB
[ERROR] C:\CODE\Test\GDI_ORIG_2022_08_03.GDB Die Datei ist bereits vorhanden.
[2022-09-02 09:52:15] End Copy
ABER:
Irgendwo hat sich ein Fehler versteckt. Das Kopieren funktioniert tadellos, die Anzeige Restzeit, Restdateien, Fortschritt stimmt zum Ende hin jedoch nicht.
Hab ich vielleicht einen Denkfehler drin? Das passiert in der Funktion "_ProgressSetCopyData", Zeile 239.
Zum Testen braucht man aber große Dateien (> 1 GB), sonst geht alles zu schnell.
Bin für eure Hilfe dankbar. Erledigt, s. Post #2
;-- TIME_STAMP 2022-09-02 09:56:19 v 0.3
#include <Array.au3>
#include <File.au3>
#include <ProgressConstants.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPIError.au3>
#include <WinAPIFiles.au3>
Opt('TrayAutoPause', 0)
OnAutoItExitRegister('_Exit')
; additional flag constants
Global Const $FC_LOGCOPY = 16 ; creates a file "TARGET\FOLDER\progresscopy.log", overwrites a possibly existing old log file
Global Const $FC_OPENLOG = 32 ; ! sets automatically the $FC_LOGCOPY flag ! - shows the logfile (if created) after the operation is finished
Global $hProgressProc = Null, $hGui, $lbPercent, $lbSpeed, $lbFile, $lbRestFiles, $lbRestSize, $lbRestTime, $hStatus, $idProgress
Global $iSumFileSize, $iTotalFileCount
Global $TransferSum = 0, $BytesTransLast = 0
Global $Transfered_halfSecond = 0
Global $TimerStart, $Timer05, $Timer1
Global $hLog = Null
; #FUNCTION# =======================================================================================
; Name ..........: _FileCopyProgress
; Description ...: Copies folders/files with graphical display
; Parameter(s)...: $_sSourcePath The file(s)/folder to copy. Wildcards allowed.
; ...............: $_sDestinationFolder The destination folder
; ....[optional].: $_vFlag Copy flags, known from "FileCopy" (Default: $FC_NOOVERWRITE)
; ...............: NEW flags:
; ...............: $FC_LOGCOPY, creates a file "TARGET\FOLDER\progresscopy.log", overwrites a possibly existing old log file
; ...............: $FC_OPENLOG, ! sets automatically the $FC_LOGCOPY flag ! - shows the logfile (if created) after the operation is finished
; ....[optional].: $_sAttribExclude Excludes files from copying by attribute (Default: 'SH')
; Return values .: Success Value > 0
; ...............: Failure 0, set @error 1 - destination folder doesn't exist
; ...............: 2 - source file has exclude attribute
; ...............: 3 - source path includes none files
; Author ........: BugFix
; Remarks .......:
; ==================================================================================================
Func _FileCopyProgress($_sSourcePath, $_sDestinationFolder, $_vFlag=0, $_sAttribExclude='SH')
Local $aExclude = StringSplit($_sAttribExclude, '', 2)
If BitAND($_vFlag, $FC_OPENLOG) Then
If Not BitAND($_vFlag, $FC_LOGCOPY) Then $_vFlag = BitOR($_vFlag,$FC_LOGCOPY)
EndIf
; Zielordner?
If Not FileExists($_sDestinationFolder) Then
If BitAND($_vFlag, $FC_CREATEPATH) Then
DirCreate($_sDestinationFolder)
Else
Return SetError(1,0,0)
EndIf
EndIf
; Log?
If BitAND($_vFlag, $FC_LOGCOPY) Then
If FileExists($_sDestinationFolder & '\progresscopy.log') Then FileDelete($_sDestinationFolder & '\progresscopy.log')
$hLog = FileOpen($_sDestinationFolder & '\progresscopy.log', BitOR($FO_UTF8_NOBOM, $FO_APPEND))
_Log($hLog, -1, StringFormat('[%4d-%02d-%02d %02d:%02d:%02d] Start Copy "%s" --> "%s"', @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC, $_sSourcePath, $_sDestinationFolder))
EndIf
; Zusammenstellen Datei(en) für Copy
Local $aFiles[2] = [1], $sAttrib = FileGetAttrib($_sSourcePath)
If ($sAttrib <> '') And (Not StringInStr($sAttrib, 'D')) Then ; is file or wildcard
Local $posBS = StringInStr($_sSourcePath, '\', 0, -1)
Local $sFileOrWildCard = StringTrimLeft($_sSourcePath, $posBS)
If StringInStr($sFileOrWildCard, '*') Or StringInStr($sFileOrWildCard, '?') Then
Local $sRoot = StringLeft($_sSourcePath, $posBS-1)
$aFiles = _FileListToArray($sRoot, $sFileOrWildCard, $FLTA_FILES, True)
Else
For $i = 0 To UBound($aExclude) -1
If StringInStr($sAttrib, $aExclude[$i]) Then Return SetError(2,0,0)
Next
$aFiles[1] = $_sSourcePath
EndIf
ElseIf ($sAttrib <> '') And (StringInStr($sAttrib, 'D')) Then ; is directory
$aFiles = _FileListToArray($_sSourcePath, '*', $FLTA_FILES, True)
EndIf
If $aFiles[0] = 0 Then Return SetError(3,0,0)
; Überprüfung und Entfernen Dateien mit Exclude-Attrib und Bestimmen Summe Dateigröße
$iSumFileSize = 0
Local $bDel
For $i = $aFiles[0] To 1 Step -1
$bDel = False
For $j = 0 To UBound($aExclude) -1
If StringInStr(FileGetAttrib($aFiles[$i]), $aExclude[$j]) Then
_ArrayDelete($aFiles, $i)
$aFiles[0] -= 1
$bDel = True
ExitLoop
EndIf
Next
If Not $bDel Then $iSumFileSize += FileGetSize($aFiles[$i])
Next
$iTotalFileCount = $aFiles[0]
If $iTotalFileCount = 0 Then Return SetError(3,0,0)
If BitAND($_vFlag, $FC_LOGCOPY) Then
_Log($hLog, -1, StringFormat('Anzahl Dateien: %d%sGesamtgröße: %s', $iTotalFileCount, @CRLF, _FormatByte($iSumFileSize, '', False, '1')))
EndIf
; Erstellen GUI
_CreateCopyGui()
GUISetState(@SW_SHOW, $hGui)
; callback für progress
; https://docs.microsoft.com/en-us/windows/win32/api/winbase/nc-winbase-lpprogress_routine
$hProgressProc = DllCallbackRegister('_ProgressProc', 'bool', 'uint64;uint64;uint64;uint64;dword;dword;handle;handle;ptr')
; Starte Kopieren
GUICtrlSetData($lbRestSize, _FormatByte($iSumFileSize, '', False, '1'))
GUICtrlSetData($lbRestFiles, $aFiles[0])
$TimerStart = TimerInit()
$Timer05 = TimerInit()
$Timer1 = TimerInit()
Local $sFileName
For $i = 1 To $aFiles[0]
$sFileName = StringTrimLeft($aFiles[$i], StringInStr($aFiles[$i], '\', 0, -1))
GUICtrlSetData($lbFile, $sFileName)
GUICtrlSetData($lbRestFiles, $aFiles[0] -($i-1))
$BytesTransLast = 0
If Not BitAND($_vFlag, $FC_OVERWRITE) Then
If FileExists($_sDestinationFolder & '\' & $sFileName) Then
If BitAND($_vFlag, $FC_LOGCOPY) Then
_Log($hLog, 0, $_sDestinationFolder & '\' & $sFileName, 'exists')
EndIf
; Korrektur Anzeige
$iSumFileSize -= FileGetSize($aFiles[$i])
GUICtrlSetData($lbRestSize, _FormatByte(($iSumFileSize - $TransferSum), '', False, '1'))
Local $iPercentTotal = Round($TransferSum / $iSumFileSize * 100)
GUICtrlSetData($idProgress, $iPercentTotal)
GUICtrlSetData($lbPercent, $iPercentTotal)
Local $iTimeRemaining = (100 - $iPercentTotal) * ((TimerDiff($TimerStart) / 1000) / $iPercentTotal)
GUICtrlSetData($lbRestTime, _FormatSeconds($iTimeRemaining))
ContinueLoop
EndIf
EndIf
If _Copy_w_Progress($aFiles[$i], $_sDestinationFolder & '\' & $sFileName, $_vFlag) Then
If BitAND($_vFlag, $FC_LOGCOPY) Then
_Log($hLog, 1, $_sDestinationFolder & '\' & $sFileName, _FormatByte(FileGetSize($aFiles[$i]) , '', False, '1'))
EndIf
EndIf
Next
If BitAND($_vFlag, $FC_LOGCOPY) Then
_Log($hLog, -1, StringFormat('[%4d-%02d-%02d %02d:%02d:%02d] End Copy', @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC))
FileClose($hLog)
$hLog = Null
EndIf
GUICtrlSetData($lbFile, '')
GUICtrlSetData($lbPercent, 100)
GUICtrlSetData($lbSpeed, 0)
GUICtrlSetData($lbRestFiles, 0)
GUICtrlSetData($lbRestSize, 0)
GUICtrlSetData($lbRestTime, 0)
GUICtrlSetData($idProgress, 100)
While True
Switch GUIGetMsg()
Case -3
GUIDelete($hGui)
If BitAND($_vFlag, $FC_OPENLOG) Then
Return ShellExecute($_sDestinationFolder & '\progresscopy.log')
Else
Return 1
EndIf
EndSwitch
WEnd
EndFunc
Func _Log($_hFile, $_iSucc=-1, $_sLeft='', $_sRight='')
Local $sSucc = $_iSucc = 0 ? '[ERROR] ' : ($_iSucc = -1 ? '' : '[SUCCESS] ')
If $_sRight <> '' Then $_sLeft = StringFormat('%-70s', $_sLeft)
If $_sRight = 'exists' Then $_sRight = 'Die Datei ist bereits vorhanden.'
Return FileWrite($_hFile, $sSucc & $_sLeft & $_sRight & @CRLF)
EndFunc
Func _CreateCopyGui()
$hGui = GUICreate('Copy with Progress', 300, 170)
GUISetBkColor(0xF0F8FF)
GUISetFont(10, 400, Default, 'Courier New')
; Prozent
GUICtrlCreateLabel('Fortschritt:', 10, 10, 115, 17)
$lbPercent = GUICtrlCreateLabel('0', 220, 10, 60, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
GUICtrlCreateLabel('%', 280, 10, 10, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; Speed
GUICtrlCreateLabel('Geschwindigkeit:', 10, 32, 125, 17)
$lbSpeed = GUICtrlCreateLabel('0 MB', 200, 32, 70, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
GUICtrlCreateLabel('/s', 270, 32, 20, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; akt. Datei
GUICtrlCreateLabel('Name:', 10, 54, 40, 17)
$lbFile = GUICtrlCreateLabel('', 55, 54, 235, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; verbleibende Elemente
GUICtrlCreateLabel('Verbleibende Elemente:', 10, 76, 180, 17)
$lbRestFiles = GUICtrlCreateLabel('', 230, 76, 60, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; verbleibende Größe
GUICtrlCreateLabel('Verbleibende Größe:', 10, 98, 180, 17)
$lbRestSize = GUICtrlCreateLabel('', 230, 98, 60, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; verbleibende Zeit
GUICtrlCreateLabel('Restdauer ungefähr:', 10, 120, 180, 17)
$lbRestTime = GUICtrlCreateLabel('', 200, 120, 90, 17, BitOR($GUI_SS_DEFAULT_LABEL, $SS_RIGHT))
GUICtrlSetColor(-1, 0x000080)
; Statusbar
$hStatus = _GUICtrlStatusBar_Create($hGui)
_GUICtrlStatusBar_SetMinHeight($hStatus, 20)
$idProgress = GUICtrlCreateProgress(0, 0, -1, -1, $PBS_SMOOTH)
GUICtrlSetColor(-1, 0x008B00) ; grün
_GUICtrlStatusBar_EmbedControl($hStatus, 0, GUICtrlGetHandle($idProgress))
EndFunc
Func _Exit()
If $hProgressProc <> Null Then DllCallbackFree($hProgressProc)
If $hLog <> Null Then FileClose($hLog)
EndFunc
Func _Copy_w_Progress($_source, $_destination, $_vFlag)
Local $flag = $COPY_FILE_FAIL_IF_EXISTS
If BitAND($_vFlag, $FC_OVERWRITE) Then $flag = 0
If Not _WinAPI_CopyFileEx($_source, $_destination, $flag, DllCallbackGetPtr($hProgressProc)) Then
If BitAND($_vFlag, $FC_LOGCOPY) Then
_Log($hLog, 0, $_source, _WinAPI_GetLastErrorMessage())
Else
_WinAPI_ShowLastError('Beim Kopieren der Datei "' & $_source & '" ist ein Fehler aufgetreten.')
EndIf
Return False
EndIf
Return True
EndFunc
Volatile Func _ProgressProc($iTotalFileSize, $iTotalBytesTransferred, $iStreamSize, $iStreamBytesTransferred, $iStreamNumber, $iCallbackReason, $hSourceFile, $hDestinationFile, $pData)
#forceref $iStreamSize, $iStreamBytesTransferred, $iStreamNumber, $iCallbackReason, $hSourceFile, $hDestinationFile, $pData
_ProgressSetCopyData($iTotalBytesTransferred)
Sleep(10)
Return $PROGRESS_CONTINUE
EndFunc
Func _ProgressSetCopyData($_BytesTrans=0)
$TransferDiff = $_BytesTrans - $BytesTransLast
$TransferSum += $TransferDiff
$BytesTransLast = $_BytesTrans
$Transfered_halfSecond += $TransferDiff
If TimerDiff($Timer05) < 500 Then Return
Local $iPercentTotal = Round($TransferSum / $iSumFileSize * 100)
If TimerDiff($Timer05) >= 500 Then ; alle 1/2 Sekunde aktualisieren
GUICtrlSetData($idProgress, $iPercentTotal)
GUICtrlSetData($lbSpeed, _FormatByte($Transfered_halfSecond * 2, '', False, '1')) ; *2: Angabe pro Sekunde
$Transfered_halfSecond = 0
$Timer05 = TimerInit()
EndIf
; andere Daten nur jede Sekunde aktualisieren
If TimerDiff($Timer1) < 1000 Then Return
GUICtrlSetData($lbPercent, $iPercentTotal)
GUICtrlSetData($lbRestSize, _FormatByte($iSumFileSize - $TransferSum, '', False, '1'))
; Restzeit: Zeit für bisher kopierte Datenmenge prozentual auf Restdatenmenge anrechnen
Local $iTimeTotal = TimerDiff($TimerStart) / 1000
Local $iTimeRemaining = (100 - $iPercentTotal) * ($iTimeTotal / $iPercentTotal)
GUICtrlSetData($lbRestTime, _FormatSeconds($iTimeRemaining))
$Timer1 = TimerInit()
EndFunc
; #FUNCTION# ====================================================================================================================
; Name ..........: _FormatSeconds
; Description ...: Returns a given value of seconds in the format
; ...............: <24h: "hh:mm:ss", >=24h: "x d / hh:mm:ss h"
; Syntax ........: _FormatSeconds($_sec)
; Parameters ....: $_sec - The number of seconds.
; Return values .: The formatted string.
; Author ........: BugFix
; ===============================================================================================================================
Func _FormatSeconds($_sec)
Return ( $_sec < 60 ? StringFormat('00:00:%02u', $_sec) : _
$_sec < 60*60 ? StringFormat('00:%02u', Floor($_sec/60)) & ':' & _
StringFormat('%02u', Mod($_sec,60)) : _
$_sec < 60*60*24 ? StringFormat('%02u', Floor($_sec/3600)) & ':' & _
StringFormat('%02u', Floor(Mod($_sec,3600)/60)) & ':' & _
StringFormat('%02u', Mod(Mod($_sec,3600),60)) : _
( $_sec = 86400 ? "24:00:00" : Floor($_sec/86400) & ' d / ' & _
StringFormat('%02u', Floor(Mod($_sec,86400)/3600)) & ':' & _
StringFormat('%02u', Floor(Mod(Mod($_sec,86400),3600)/60)) & ':' & _
StringFormat('%02u', Mod(Mod(Mod($_sec,86400),3600),60)) & ' h') )
EndFunc ;==>_FormatSeconds
; #FUNCTION# ====================================================================================================================
; Name ..........: _FormatByte
; Description ...: Formats a given value of bytes with highest or given unit, optional as structure with all units
; Parameters ....: $_iByte The value of bytes to format
; ...............: $_sUnit (Default = '', unit of highest value) or count of given unit (TB, GB, MB, KB, Byte)
; ...............: $_bStruct Returns a structure with .TB .GB .MB .KB .Byte (Default = False)
; ...............: $_sDigit Number of decimal digits (Default = '3') as string!
; Return values .: The formatted string or the structure.
; Author ........: BugFix
; ===============================================================================================================================
Func _FormatByte($_iByte, $_sUnit='', $_bStruct=False, $_sDigit='3')
Local Static $aByte[5][2] = [[0x10000000000],[0x40000000],[0x100000],[0x400],[0x1]]
Local Static $tBytes = DllStructCreate('int TB;int GB;int MB;int KB;int Byte;')
Local Static $aUnit[5] = ['TB','GB','MB','KB','Byte']
Local $iModulo = $_iByte, $iHighest = 4
For $i = 0 To 3
$aByte[$i][1] = $iModulo >= $aByte[$i][0] ? Floor($iModulo/$aByte[$i][0]) : 0
$iModulo = $aByte[$i][1] > 0 ? Mod($iModulo,$aByte[$i][0]) : $iModulo
$iHighest = $aByte[$i][1] > 0 ? ($i < $iHighest ? $i : $iHighest) : $iHighest
Next
$aByte[4][1] = $iModulo
If $_bStruct Then
$tBytes.TB = $aByte[0][1]
$tBytes.GB = $aByte[1][1]
$tBytes.MB = $aByte[2][1]
$tBytes.KB = $aByte[3][1]
$tBytes.Byte = $aByte[4][1]
Return $tBytes
EndIf
$_sUnit = StringInStr('TB GB MB KB Byte', $_sUnit) ? $_sUnit : ''
$_sUnit = $_sUnit = '' ? $aUnit[$iHighest] : $_sUnit
Local $iUserUnit = Floor(StringInStr('TB GB MB KB Byte', $_sUnit)/3)
If Number($_sDigit) < 0 Then $_sDigit = '0'
Local $sFormat = '%.' & $_sDigit & 'f %s'
Return StringFormat($sFormat, $_iByte/$aByte[$iUserUnit][0], $aUnit[$iUserUnit])
EndFunc ;==>_FormatByte
Alles anzeigen