Nikolas92

Copy with progress bar?

8 posts in this topic

#1 ·  Posted (edited)

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

Share this post


Link to post
Share on other sites



#2 ·  Posted

Bump. What parameter of _WinAPI_CopyFileEx is invalid (error 87)? thanks

Share this post


Link to post
Share on other sites

#3 ·  Posted

Hi.

Call _WinAPI_GetLastError() to get extended error information.

Conrad


SciTE = 3.6.2.0/full   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   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.

Share this post


Link to post
Share on other sites

#4 ·  Posted

6 minutes ago, Simpel said:

Hi.

Call _WinAPI_GetLastError() to get extended error information.

Conrad

Thats already in example. How would I know otherwise what error number is?

Share this post


Link to post
Share on other sites

#5 ·  Posted

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

_WinAPI_GetLastErrorMessage() - this is mostly explaining

I hope this is the right function, Conrad


SciTE = 3.6.2.0/full   AutoIt = 3.3.14.2   AutoItX64 = 0   OS = Win7Pro SP1   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.

Share this post


Link to post
Share on other sites

#6 ·  Posted

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.

Share this post


Link to post
Share on other sites

#7 ·  Posted

bump

Share this post


Link to post
Share on other sites

#8 ·  Posted

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?

Share this post


Link to post
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