Jump to content

Copy with progress bar?


Recommended Posts

Hi, i am trying to make copying directory to show progress bar, but function _WinAPI_CopyFileEx randomly fails with error 87. Could someone help me find what is causing this error?

; www.autoitscript.com/forum/topic/158598-dllcallbackregister-with-non-freezing-gui-volatile-example
; www.autoitscript.com/forum/topic/153104-scriptdir-returns-trailing-slash

#NoTrayIcon
#RequireAdmin
#include <File.au3>
#include <Constants.au3>
#include <GUIConstants.au3>
#include <WinAPI.au3>
#include <WinAPIFiles.au3>
#include <WinAPIShPath.au3>
#include <WinAPIShellEx.au3>
#include <Security.au3>

Opt("WinWaitDelay", 0)
Opt("MouseClickDelay", 0)
Opt("MouseClickDownDelay", 0)
Opt("MouseClickDragDelay", 0)
Opt("SendKeyDelay", 0)
Opt("SendKeyDownDelay", 0)
Opt("WinTitleMatchMode", 3)
Opt("GUIOnEventMode", 1)
FileChangeDir(StringRegExpReplace(@ScriptDir, '\\+$', ''))

If $CmdLine[0] = 0 Then
   MsgBox($MB_OK, 'Info', 'Drag files and folders on ' & @ScriptName & ' to test copying with progress bar.')
   Exit
EndIf

Global Const $SID_ALL_APP_PACKAGES_GROUP = 'S-1-15-2-1'
Global $MetroGroupExist = False
Global $AccountInfo = _Security__LookupAccountSid($SID_ALL_APP_PACKAGES_GROUP)
If IsArray($AccountInfo) Then $MetroGroupExist = True

Global $hProgressProc = DllCallbackRegister('_ProgressProc', 'bool', 'uint64;uint64;uint64;uint64;dword;dword;handle;handle;ptr')
Global $FailedToCopyList = ''
Global $Folders[1] = [1]
Global $Files[1] = [1]
Global $TotalSize = 0
Global $CopiedSize = 0
Global $TargetDirCreated = False
Global $Drive, $Directory, $Name, $Extension
Global $GuiName = 'Copying files and folders...'
Global $MainGui = GUICreate($GuiName, 250, 59, (@DesktopWidth-250)/2-35, 0, BitOR($WS_CAPTION, $WS_SYSMENU))
Global $progressbar = GUICtrlCreateProgress(5, 5, 240, 20, $PBS_SMOOTH)
Global $CurrentActivity = GUICtrlCreateLabel('', 5, 27, 240, 30, BitOR($SS_CENTER, $BS_MULTILINE))
GUISetOnEvent($GUI_EVENT_CLOSE, '_AllExit', $MainGui)
GUICtrlSetFont($CurrentActivity, 9, 0, 0, "Verdana")
GUICtrlSetData($CurrentActivity, 'Calculating total size...')
GUISetState(@SW_SHOW, $MainGui)

$aCmdLine = _WinAPI_CommandLineToArgv($CmdLineRaw)
For $i = 1 to $aCmdLine[0]
   $Path = $aCmdLine[$i]
   If StringRight($Path, 1) = '\' or StringRight($Path, 1) = '"' Then $Path = StringTrimRight($Path, 1)
   If not StringInStr(FileGetAttrib($Path), "D") Then
      $Files[0] += 1
      ReDim $Files[$Files[0]]
      $Files[$Files[0] - 1] = $Path
      $iSize = _GetFileSize($Path)
      If $iSize <> -1 Then $TotalSize = $TotalSize + $iSize
   Else
      $Folders[0] += 1
      ReDim $Folders[$Folders[0]]
      $Folders[$Folders[0] - 1] = $Path
      $iSize = DirGetSize($Path)
      $TotalSize = $TotalSize + $iSize
   EndIf
Next

If UBound($Folders) - 1 > 0 Then
   For $i = 1 to UBound($Folders) - 1
      _MultiFileCopy($Folders[$i], @DesktopDir & '\test')
   Next
EndIf
If UBound($Files) - 1 > 0 Then
   For $i = 1 to UBound($Files) - 1
      _MultiFileCopy($Files[$i], @DesktopDir & '\test')
   Next
EndIf
GUISetState(@SW_HIDE, $MainGui)
If $FailedToCopyList <> '' Then MsgBox($MB_OK, 'Info', 'Failed to copy:' & @CRLF & $FailedToCopyList)
Exit

Volatile Func _AllExit()
   GUIDelete(@GUI_WinHandle)
   Exit
EndFunc

Volatile Func _ProgressProc($iTotalFileSize, $iTotalBytesTransferred, $iStreamSize, $iStreamBytesTransferred, $iStreamNumber, $iCallbackReason, $hSourceFile, $hDestinationFile, $pData)
   $iPercent = (($CopiedSize+$iTotalBytesTransferred)/$TotalSize)*100
   GUICtrlSetData($progressbar, Round($iPercent))
   If $iTotalBytesTransferred = $iTotalFileSize Then $CopiedSize = $CopiedSize + $iTotalFileSize
EndFunc

Func _GetFileSize($sPath)
   $hFile = _WinAPI_CreateFileEx('\\.\' & $sPath, $OPEN_EXISTING, 0, BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), BitOR($FILE_FLAG_BACKUP_SEMANTICS, $FILE_FLAG_OPEN_REPARSE_POINT))
   $sSize = _WinAPI_GetFileSizeEx($hFile)
   _WinAPI_CloseHandle($hFile)
   Return $sSize
EndFunc

Func _MultiFileCopy($sSourcePath, $sTargetDir, $RemoveTargetIfExist = False, $CreateSourceRootFolderInTarget = True)
   Local $sDrive, $sDir, $sFileName, $sExtension
   _PathSplit($sTargetDir, $sDrive, $sDir, $sFileName, $sExtension)
   $sTargetDrive = $sDrive
   If $TargetDirCreated = False Then
      If $RemoveTargetIfExist = True and DriveGetDrive($sTargetDrive) <> 'CDROM' Then _DeleteTarget($sTargetDir)
      _WinAPI_CreateDirectory($sTargetDir)
      $TargetDirCreated = True
   EndIf
   _PathSplit($sSourcePath, $sDrive, $sDir, $sFileName, $sExtension)
   Switch _WinAPI_PathIsDirectory($sSourcePath)
   Case False
      If _WinAPI_FileExists($sSourcePath) = True Then
         GUICtrlSetData($CurrentActivity, 'Copying file ' & $sFileName & $sExtension & '...')
         If _WinAPI_FileExists($sTargetDir & '\' & $sFileName & $sExtension) = True and DriveGetDrive($sTargetDrive) <> 'CDROM' Then _DeleteTarget($sTargetDir & '\' & $sFileName & $sExtension)
         _CopyFileProc($sSourcePath, $sTargetDir & '\' & $sFileName & $sExtension)
      Else
         Return SetError(1, 0, False)
      EndIf
   Case $FILE_ATTRIBUTE_DIRECTORY
      $sTarget = $sTargetDir
      If $CreateSourceRootFolderInTarget = True Then
         If ($sFileName & $sExtension) <> '' Then
            $sTargetDir = $sTargetDir & '\' & $sFileName & $sExtension
         Else
            $sTargetDir = $sTargetDir & '\Drive ' & StringUpper(StringReplace($sDrive, ':', ''))
         EndIf
      EndIf
      If ($sFileName & $sExtension) <> '' Then
         GUICtrlSetData($CurrentActivity, 'Copying folder ' & $sFileName & $sExtension & '...')
      Else
         GUICtrlSetData($CurrentActivity, 'Copying drive ' & StringUpper($sDrive) & '...')
      EndIf
      If _WinAPI_PathIsDirectory($sTargetDir) = $FILE_ATTRIBUTE_DIRECTORY Then
         If ($sTarget <> $sTargetDir or ($RemoveTargetIfExist = True and $TargetDirCreated = False)) and DriveGetDrive($sTargetDrive) <> 'CDROM' Then _DeleteTarget($sTargetDir)
      EndIf
      _MultiFileCopyDirProc($sSourcePath, $sTargetDir)
      $aFolderList = _FileListToArrayRec($sSourcePath, '*', $FLTAR_FOLDERS, $FLTAR_RECUR, $FLTAR_SORT, $FLTAR_FULLPATH)
      If IsArray($aFolderList) Then
         For $i = 1 to $aFolderList[0]
            _MultiFileCopyDirProc($aFolderList[$i], $sTargetDir & '\' & StringReplace($aFolderList[$i], $sSourcePath & '\', ''))
         Next
      EndIf
   EndSwitch
   Return True
EndFunc

Func _DeleteTarget($sTarget)
   _WinAPI_ShellFileOperation($sTarget, '', $FO_DELETE, BitOR($FOF_NOCONFIRMATION, $FOF_NOERRORUI, $FOF_SILENT, $FOF_NO_UI))
   If @extended = 120 Then
      _SubinaclFull($sTarget)
      _WinAPI_ShellFileOperation($sTarget, '', $FO_DELETE, BitOR($FOF_NOCONFIRMATION, $FOF_NOERRORUI, $FOF_SILENT, $FOF_NO_UI))
   EndIf
   If @error Then Return False
   Return True
EndFunc

Func _MultiFileCopyDirProc($sSourcePath, $sTargetDir)
   _WinAPI_CreateDirectory($sTargetDir)
   $aFileList = _FileListToArrayRec($sSourcePath, '*', $FLTAR_FILES, $FLTAR_NORECUR, $FLTAR_NOSORT, $FLTAR_FULLPATH)
   If IsArray($aFileList) Then
      For $j = 1 to $aFileList[0]
         _CopyFileProc($aFileList[$j], $sTargetDir & '\' & StringReplace($aFileList[$j], $sSourcePath & '\', ''))
      Next
   EndIf
EndFunc

Func _CopyFileProc($sSourceFile, $sTargetFile)
   $result = _WinAPI_CopyFileEx($sSourceFile, $sTargetFile, BitOR($COPY_FILE_ALLOW_DECRYPTED_DESTINATION, $COPY_FILE_COPY_SYMLINK, $COPY_FILE_NO_BUFFERING), DllCallbackGetPtr($hProgressProc))
   If _WinAPI_GetLastError() = 5 Then
      _SubinaclFull($sSourceFile)
      $result = _WinAPI_CopyFileEx($sSourceFile, $sTargetFile, BitOR($COPY_FILE_ALLOW_DECRYPTED_DESTINATION, $COPY_FILE_COPY_SYMLINK, $COPY_FILE_NO_BUFFERING), DllCallbackGetPtr($hProgressProc))
   EndIf
   If $result = False Then
      $FailedToCopyList = $FailedToCopyList & $sSourceFile & ' (error ' & _WinAPI_GetLastError() & ')' & @CRLF
      $iSize = _GetFileSize($sSourceFile)
      If $iSize <> -1 Then
         $CopiedSize = $CopiedSize + $iSize
         $iPercent = ($CopiedSize/$TotalSize)*100
         GUICtrlSetData($progressbar, Round($iPercent))
      EndIf
   EndIf
EndFunc

Func _SubinaclFull($sPath, $recurse = True)
   $MetroGroupAce = ''
   If $MetroGroupExist = True Then $MetroGroupAce = ' /GRANT="ALL APPLICATION PACKAGES"=R'
   If StringRight($sPath, 1) <> '\' Then $sPath = $sPath & '\'
   $PathForSubinacl = StringTrimRight(StringReplace($sPath, @WindowsDir & '\System32\', @WindowsDir & '\Sysnative\'), 1)
   RunWait(StringRegExpReplace(@WorkingDir, '\\+$', '') & '\subinacl.exe' & ' /nostatistic /noverbose /file "' & $PathForSubinacl & '" /GRANT=System=F /GRANT=Administrators=F /GRANT=Users=F /GRANT=Everyone=F /GRANT=' & @UserName & '=F' & $MetroGroupAce & ' /SETOWNER=Everyone', '', @SW_HIDE)
   If $recurse = True and StringInStr(FileGetAttrib(StringTrimRight($sPath, 1)), "D") Then RunWait(StringRegExpReplace(@WorkingDir, '\\+$', '') & '\subinacl.exe' & ' /nostatistic /noverbose /subdirectories "' & $PathForSubinacl & '\*" /GRANT=System=F /GRANT=Administrators=F /GRANT=Users=F /GRANT=Everyone=F /GRANT=' & @UserName & '=F' & $MetroGroupAce & ' /SETOWNER=Everyone', '', @SW_HIDE)
EndFunc

 

Edited by Nikolas92
Link to comment
Share on other sites

Hi.

Call _WinAPI_GetLastError() to get extended error information.

Conrad

SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win_10   Build = 19044   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Link to comment
Share on other sites

Sorry, currently no AutoIt to test. What I meant is:

_WinAPI_GetLastErrorMessage() - this is mostly explaining

I hope this is the right function, Conrad

SciTE4AutoIt = 3.7.3.0   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win_10   Build = 19044   OSArch = X64   Language = 0407/german
H:\...\AutoIt3\SciTE     H:\...\AutoIt3      H:\...\AutoIt3\Include     (H:\ = Network Drive)

   88x31.png  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind.

Link to comment
Share on other sites

16 minutes ago, Simpel said:

Sorry, currently no AutoIt to test. What I meant is:

_WinAPI_GetLastErrorMessage() - this is mostly explaining

I hope this is the right function, Conrad

I already tried that too and result was that some parameter is incorrect.

Link to comment
Share on other sites

When I want to show a progress bar while copying files and/or directories, I use Yashied's Copy UDF. I've used it to write a script that we us internally where I work to copy all data from one machine to a storage device and notifies the user of any folders in the root of C: that are not standard (so they might be copied as well)

 

Who lied and told you life would EVER be fair?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...