- Offizieller Beitrag
Ich brauchte gerade eine Kopierfunktion für mehrere Dateien und Tom99 hatte in "Hilfe & Unterstützung" eine Copy-Funktion mit DllCallback gepostet. Da habe ich die mal etwas ausgebaut:
Spoiler anzeigen
;===werden benötigt=============================================================
#include<String.au3>
#include<Misc.au3>
Global $strFiles = '', $strSize = 0
;===============================================================================
;===Beispiel 1 (erstellen der Datei "!copydata.mfc" im Quellverzeichnis)========
Global $sSourcePath = 'C:\Programme\AutoIt3\Examples\'
Global $aReturn = _MultiFileCopy($sSourcePath)
[/autoit] [autoit][/autoit] [autoit]MsgBox(0, 'MFC', '"!copydata.mfc" wurde erstellt!')
;===============================================================================
;===Beispiel 2 (komplettes Verzeichnis kopieren)================================
; Alle Dateien und Unterverzeichnisse (rekursiv) werden kopiert
; Verzeichnisstruktur der Quelle wird übernommen
Global $sSourcePath = 'C:\Programme\AutoIt3\Examples\'
Global $sDestinationPath = @ScriptDir & '\Tmp1'; Zielpfad (wird ggf. erstellt)
Global $aReturn = _MultiFileCopy($sSourcePath, $sDestinationPath, False)
;===============================================================================
;===Beispiel 3 (mehrere Dateien kopieren)=======================================
; Alle Dateien aus dem Array werden ins Zielverzeichnis kopiert
; Array[0] muss die Anzahl der zu kopierenden Dateien enthalten
Global $aSource[4] = [3, @SystemDir & '\shell32.dll', @SystemDir & '\mspaint.exe', @SystemDir & '\eula.txt']
Global $sDestinationPath = @ScriptDir & '\Tmp2'; Zielpfad (wird ggf. erstellt)
Global $aReturn = _MultiFileCopy($aSource, $sDestinationPath, False)
;===============================================================================
;===============================================================================
; Function Name: _MultiFileCopy
; Description:: Kopiert mehrere Dateien mit Progressbar
; Parameter(s): Array mit Dateien zum kopieren oder Quellpfad,
; Zielpfad,
; überschreiben? [True/False],
; Wenn überschreiben = True, dann PreFix zum überschreiben
; Return Value(s): Array mit den kopierten Dateien
; Requirement(s): Global $strFiles = '', $strSize = 0
; #include<String.au3>
; #include<Misc.au3>
; Author(s): Tom99, progandy, eukalyptus and Oscar (http://www.autoit.de)
;===============================================================================
Func _MultiFileCopy($aSource, $sDestPath = '', $bOverWrite = True, $sPreFix = '!Copy')
Local $oldGUICloseOnESC = Opt('GUICloseOnESC', 0)
Local $ret, $sShowSource, $sShowDest, $sSourcePath = '', $sNewFolder = '', $k
Local $aMFC[12] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, False, DllOpen('user32.dll')]
If IsArray($aSource) Then
If Not IsNumber($aSource[0]) Then Return SetError(1, 0, 0)
For $i = 1 To $aSource[0]
$aMFC[7] += FileGetSize($aSource[$i])
Next
Else
$sSourcePath = $aSource
If StringRight($sSourcePath, 1) <> '\' Then $sSourcePath &= '\'
$strSize = 0
ToolTip('Bitte warten! Verzeichnis wird eingelesen!', @DesktopWidth / 2 - 100, 10)
If FileExists($sSourcePath & '!copydata.mfc') And $sDestPath <> '' Then
Local $sFile = StringTrimRight(FileRead($sSourcePath & '!copydata.mfc'), 2)
$strSize = StringLeft($sFile, StringInStr($sFile, @CRLF) - 1)
$aSource = StringSplit(StringTrimLeft($sFile, StringInStr($sFile, @CRLF) + 1), @CRLF, 1)
Else
If FileExists($sSourcePath & '!copydata.mfc') Then FileDelete($sSourcePath & '!copydata.mfc')
$aSource = _GetFilesFolder_Rekursiv($sSourcePath)
Local $hFile = FileOpen($sSourcePath & '!copydata.mfc', 2)
If $hFile <> -1 Then
FileWriteLine($hFile, $strSize)
For $i = 1 To $aSource[0]
FileWriteLine($hFile, $aSource[$i])
Next
FileClose($hFile)
EndIf
EndIf
$aMFC[7] = $strSize
ToolTip('')
If $sDestPath = '' Then Return SetError(0, 0, 1)
EndIf
If StringRight($sDestPath, 1) <> '\' Then $sDestPath &= '\'
If Not FileExists($sDestPath) Then
If Not DirCreate($sDestPath) Then Return SetError(2, 0, 0)
EndIf
$sShowDest = StringRegExpReplace($sDestPath, '(.{15})(.*)(.{35})', '$1' & '[...]' & '$3')
Local $aReturn = $aSource
Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')
Local $ptr = DllCallbackGetPtr($callback)
Local $DllKernel32 = DllOpen('kernel32.dll')
__ProgressCreate($aMFC)
$aMFC[9] = TimerInit()
For $i = 1 To $aSource[0]
$sArray = ''
For $j = 0 To 11
$sArray &= $aMFC[$j] & ';'
Next
$sFile = StringMid($aSource[$i], StringInStr($aSource[$i], '\', 0, -1) + 1)
If $sSourcePath <> '' Then
$sNewFolder = StringTrimLeft(StringLeft($aSource[$i], StringInStr($aSource[$i], '\', 0, -1)), StringLen($sSourcePath))
If Not FileExists($sDestPath & $sNewFolder) Then
If Not DirCreate($sDestPath & $sNewFolder) Then Return SetError(3, 0, 0)
EndIf
EndIf
If $sFile = '' Then ContinueLoop
$k = 0
While $bOverWrite = False And FileExists($sDestPath & $sNewFolder & $sFile)
$k += 1
$sFile = $sPreFix & $k & "_" & StringMid($aSource[$i], StringInStr($aSource[$i], '\', 0, -1) + 1)
WEnd
$aReturn[$i] = $sDestPath & $sNewFolder & $sFile
$sShowSource = StringRegExpReplace($aSource[$i], '(.{15})(.*)(.{35})', '$1' & '[...]' & '$3')
GUICtrlSetData($aMFC[1], 'Kopiere Datei ' & @CRLF & '"' & $sShowSource & '"' & @CRLF & 'nach: "' & $sShowDest & '"')
$ret = DllCall($DllKernel32, 'int', 'CopyFileExA', 'str', $aSource[$i], 'str', $aReturn[$i], 'ptr', $ptr, 'str', $sArray, 'int', 0, 'int', 0)
;~ ConsoleWrite('Return: ' & $ret[0] & @LF)
If $ret[0] = 0 Then $aMFC[10] = True
$aMFC[8] += FileGetSize($aSource[$i])
Next
DllClose($DllKernel32)
DllCallbackFree($callback)
GUIDelete($aMFC[0])
DllClose($aMFC[11])
Opt('GUICloseOnESC', $oldGUICloseOnESC)
Return $aReturn
EndFunc ;==>_MultiFileCopy
Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hDestinationFile, $lpData)
Local $aSplit = StringSplit(StringTrimRight($lpData, 1), ";")
If $aSplit[11] = 'True' Then Return 1
Local $pos = GUIGetCursorInfo($aSplit[1])
If _IsPressed('1B', Int($aSplit[12])) Then Return 1
If _IsPressed('01', Int($aSplit[12])) And ($pos[4] = Int($aSplit[7])) Then Return 1
Local $sPercent = Round($BytesTransferred / $FileSize * 100, 0), $iTime, $iTotalTime, $iTransferRate
Local $sPercentAll = Round(($aSplit[9] + $BytesTransferred) / $aSplit[8] * 100, 0)
$iTime = TimerDiff($aSplit[10])
$iTotalTime = Ceiling($iTime / 1000 / ($sPercentAll + 0.1) * 100)
$iTransferRate = _StringAddThousandsSep(Int($aSplit[8] / $iTotalTime / 1000), '.', ',')
GUICtrlSetData($aSplit[3], $sPercent & ' %')
GUICtrlSetData($aSplit[5], $sPercent)
GUICtrlSetData($aSplit[4], $sPercentAll & ' % Zeit: ' & Int($iTime / 1000) & '/' & $iTotalTime & ' s (' & $iTransferRate & ' KB/s)')
GUICtrlSetData($aSplit[6], $sPercentAll)
EndFunc ;==>__Progress
Func __ProgressCreate(ByRef $aMFC)
If Not IsDeclared('WS_POPUPWINDOW') Then Local Const $WS_POPUPWINDOW = 0x80880000
If Not IsDeclared('WS_EX_TOPMOST') Then Local Const $WS_EX_TOPMOST = 0x00000008
If Not IsDeclared('WS_EX_TOOLWINDOW') Then Local Const $WS_EX_TOOLWINDOW = 0x00000080
If Not IsDeclared('WS_EX_COMPOSITED') Then Local Const $WS_EX_COMPOSITED = 0x02000000
$aMFC[0] = GUICreate('MultiFileCopy', 480, 220, -1, -1, $WS_POPUPWINDOW, BitOR($WS_EX_TOPMOST, $WS_EX_TOOLWINDOW, $WS_EX_COMPOSITED))
$aMFC[1] = GUICtrlCreateLabel('', 10, 10, 460, 65)
GUICtrlSetFont(-1, 10, 400, 0, 'Courier New')
GUICtrlCreateLabel('Datei:', 10, 83, 60, 16)
GUICtrlSetFont(-1, 11, 600, 0, 'Courier New')
$aMFC[2] = GUICtrlCreateLabel('0 %', 80, 83, 390, 16)
GUICtrlSetFont(-1, 11, 600, 0, 'Courier New')
$aMFC[4] = GUICtrlCreateProgress(10, 100, 460, 20)
GUICtrlCreateLabel('Gesamt:', 10, 133, 60, 16)
GUICtrlSetFont(-1, 11, 600, 0, 'Courier New')
$aMFC[3] = GUICtrlCreateLabel('0 %', 80, 133, 390, 16)
GUICtrlSetFont(-1, 11, 600, 0, 'Courier New')
$aMFC[5] = GUICtrlCreateProgress(10, 150, 460, 20)
$aMFC[6] = GUICtrlCreateButton('Abbrechen', 200, 185, 75, 25)
GUICtrlSetFont(-1, 9, 400, 0, 'Arial')
GUISetState()
EndFunc ;==>__ProgressCreate
;==================================================================================================
; Function Name: _GetFilesFolder_Rekursiv($sPath [, $sExt='*' [, $iDir=-1 [, $iRetType=0 ,[$sDelim='0']]]])
; Description: Rekursive Auflistung von Dateien und/oder Ordnern
; Anpassung: Verzeichnisgröße ermitteln für _MultiFileCopy ($strSize)
; Parameter(s): $sPath der Basispfad für die Auflistung ('.' -aktueller Pfad, '..' -Parentpfad)
; $sExt Erweiterung für Dateiauswahl '*' oder -1 für alle (Standard)
; $iDir -1 Dateien+Ordner(Standard), 0 nur Dateien, 1 nur Ordner
; optional: $iRetType 0 gibt Array, 1 gibt String zurück
; optional: $sDelim legt Trennzeichen für Stringrückgabe fest
; 0 -@CRLF (Standard) 1 -@CR 2 -@LF 3 -';' 4 -'|'
; Return Value(s): Array (Standard) od. String mit den gefundenen Pfaden der Dateien und/oder Ordner
; Array[0] enthält die Anzahl der gefundenen Dateien/Ordner
; Author(s): BugFix ([email='bugfix@autoit.de'][/email])
;==================================================================================================
Func _GetFilesFolder_Rekursiv($sPath, $sExt='*', $iDir=-1, $iRetType=0, $sDelim='0')
Global $oFSO = ObjCreate('Scripting.FileSystemObject')
Global $strFiles = ''
Switch $sDelim
Case '1'
$sDelim = @CR
Case '2'
$sDelim = @LF
Case '3'
$sDelim = ';'
Case '4'
$sDelim = '|'
Case Else
$sDelim = @CRLF
EndSwitch
If ($iRetType < 0) Or ($iRetType > 1) Then $iRetType = 0
If $sExt = -1 Then $sExt = '*'
If ($iDir < -1) Or ($iDir > 1) Then $iDir = -1
_ShowSubFolders($oFSO.GetFolder($sPath),$sExt,$iDir,$sDelim)
If $iRetType = 0 Then
Local $aOut
$aOut = StringSplit(StringTrimRight($strFiles, StringLen($sDelim)), $sDelim, 1)
If $aOut[1] = '' Then
ReDim $aOut[1]
$aOut[0] = 0
EndIf
Return $aOut
Else
Return StringTrimRight($strFiles, StringLen($sDelim))
EndIf
EndFunc
Func _ShowSubFolders($Folder, $Ext='*', $Dir=-1, $Delim=@CRLF)
If Not IsDeclared("strFiles") Then Global $strFiles = ''
If ($Dir = -1) Or ($Dir = 0) Then
For $file In $Folder.Files
If $Ext <> '*' Then
If StringRight($file.Name, StringLen($Ext)) = $Ext Then
$strSize += $file.size
$strFiles &= $file.Path & $Delim
EndIf
Else
$strSize += $file.size
$strFiles &= $file.Path & $Delim
EndIf
Next
EndIf
For $Subfolder In $Folder.SubFolders
If ($Dir = -1) Or ($Dir = 1) Then $strFiles &= $Subfolder.Path & '\' & $Delim
_ShowSubFolders($Subfolder, $Ext, $Dir, $Delim)
Next
EndFunc
ScreenShot:
IOM302_fix.zip
- Die Funktion kann auch ein ganzes Verzeichnis (rekursiv) kopieren, wenn als Quelle kein Array, sondern ein Verzeichnispfad angegeben wird.
- Es wird dann die Verzeichnisstruktur des Übergabepfades übernommen und im Zielpfad erstellt!
- Beim kopieren einzelner Dateien muss im Übergabe-Array (für die zu kopierenden Dateien) der erste Eintrag die Anzahl der Dateien beinhalten!
- Der Zielpfad wird erstellt, wenn er nicht existiert!
- Abbruch per [ESC]-Taste oder über den [Abbrechen]-Button!
- Wird der dritte Parameter auf "False" gesetzt werden evtl. vorhandene Dateien nicht überschrieben. Die neuen Dateien bekommen dann einen PreFix vorangestellt, den man als 4. Parameter angeben kann.
- Die Funktion gibt ein Array mit den kopierten Dateien zurück.
Im Script befinden sich drei Beispiele, die den Aufruf verdeutlichen sollen.
Zum rekursiven einlesen des Quellverzeichnisses benutze ich (mal wieder) die Funktion _GetFilesFolder_Rekursiv() von BugFix.
Neue Version ( 26.01.09 )
Weil das einlesen der Verzeichnisstruktur u.U. so lange dauern kann, habe ich MultiFileCopy (MFC) dahingehend geändert, dass man vorab (einmalig) diese Daten einlesen kann (siehe Beispiel). Dazu muss man die Funktion nur mit dem Quellverzeichnis (ohne weitere Parameter) aufrufen. Die Daten werden dann in einer Datei "!copydata.mfc" im Quellverzeichnis abgelegt. Will man später dieses Verzeichnis mit MultiFileCopy irgendwohin kopieren, so sieht MFC nach, ob sich im Quellverzeichnis eine solche Datei befindet und liest die Daten aus dieser Datei, statt das Verzeichnis zeitaufwendig neu einzulesen.
Achtung! Wenn sich die Dateien und/oder die Ordnerstruktur geändert haben, so muss man die Datei "!copydata.mfc" neu erstellen lassen, ansonsten würde MFC die "alten" Daten benutzen.