Jump to content

Filecopy() + Something Else


Recommended Posts

I've been creating a bunch of random GUIs just to mess around and help me think of better ways to make useful programs later on.

I'm currently playing with a simple program to copy a file from one place to another.

What I had originally wanted to do was start a filecopy, display a progress bar, and inform the user the copy was complete. I've found a few Filecopy-Progress bar scripts floating around on the boards, but they are for folder copying and multiple files. The last one I tried technically worked as far as the progress bar went, but if you only copied one file, the destiation file always ended up getting corrupted. I was looking to copy a single file and show progress. The reason why is because the file can be anywhere up to 50MB, copying across a really slow network share. A progress bar staying at 1% and then jumping to 100% 2 minutes later is rather unhelpful.

So what I decided to do was provide the user some random form of visual feedback to show that it was actually doing something. I settled on using radio buttons in a row (Why? I don't know, just wanted to see how one would go about doing it).

So ultimately, I want this to display the animation while the file is being copied, and stop when the copy is complete.

#Include <GUIConstants.au3>

$ThisApp = "Update Tool"

$App = GUICreate($ThisApp, 400, 200, @DesktopWidth / 2 - 200, @DesktopHeight / 2 - 100, $WS_POPUPWINDOW)
$Button_Update = GUICtrlCreateButton("Update", 50, 145, 75, 35)
$Button_Close = GUICtrlCreateButton("Close", 275, 145, 75, 35)
$Input_Status = GUICtrlCreateInput("Click update to download the latest version", 75, 100, 250, 20, $ES_CENTER + $ES_READONLY)
$Source = "C:\Test\From\Spreadsheet.xls"
$Destination = "C:\Test\To\Spreadsheet.xls"
$X = 50
$Y = 60
$Z = 1
$Buttons = 21
Global $Radio[$Buttons + 1] = [0]
GUISetState()

For $i = 1 To $Buttons
    Sleep(20)
    $Radio[$i] = GUICtrlCreateRadio("", $X, $Y)
    GUICtrlSetState($Radio[$i], $GUI_DISABLE)
    $X = $X + 300 / $Buttons
    If $Z = 1 Then
        $Y = $Y - 1
        $Z = 0
    Else
        $Z = 1
    EndIf
Next

While 1
    $msg = GUIGetMsg()
    If $msg = $Button_Close Then Exit
    If $msg = $Button_Update Then
        $Z = 0
        GUICtrlSetState($Button_Update, $GUI_DISABLE)
        GUICtrlSetData($Input_Status, "Downloading")
    ;FileCopy($Source, $Destination, 1)
        While $Z < 3                ; While file is copying... possible?
            GUICtrlSetState($Radio[$Buttons], $GUI_DISABLE)
            For $i = 1 To $Buttons
                If GUIGetMsg() = $Button_Close Then Abort()
                GUICtrlSetState($Radio[$i], $GUI_ENABLE + $GUI_CHECKED)
                If $i - 1 <> 0 Then
                    GUICtrlSetState($Radio[$i - 1], $GUI_DISABLE + $GUI_UNCHECKED)
                EndIf
                Sleep(50)
            Next
            $Z = $Z + 1
        WEnd
        GUICtrlSetState($Button_Update, $GUI_ENABLE)
        GUICtrlSetData($Input_Status, "Update Complete")
    EndIf
WEnd

Func Abort()
    MsgBox(262160, "Warning", "Update Aborted", 2)
    Exit
EndFunc

While it would be nice of some sort of progress bar to display the actual progress, I'm not really looking to do that here (Though it would be nice to know how later on)

A shorter way to describe it, if you don't care about the poorly written code above, would be:

FileCopyAndStartTheNextLineOfCodeBeforeTheFileIsFinished($Source, $Dest)

While FileIsCopying

MiscProgressDisplay

Wend

Exit

Hmm, that's even worse =)

Anyway, if anyone has any advice or a slap in the face for me, feel free to let me know. Any input would be appreciated.

Link to comment
Share on other sites

I don't think you can do what you try to accomplish with the FileCopy() function, the easiest way I can see is that you read the file into your script (binary) and then write the file att the destination, that way you can see how much of the copying has been completed.

Broken link? PM me and I'll send you the file!

Link to comment
Share on other sites

Anyways here's an axample how you can copy one file with progress:

$filehandle = FileOpen("source.file",16) ; Open in read + binary mode
$output = FileOpen("destination.file",17); Open in write + binary mode
$chunksize=100 ; How many bytes the script will read at a time
$size = FileGetSize("source.file") 

$i=0
Do
$data = FileRead($filehandle,$chunksize)

If $data <>"" Then FileWrite($output,$data)
$i+=$chunksize

ConsoleWrite(Round($i/$size*100)&" %"&@CRLF) ; Output progress in console

Until $data=""

Broken link? PM me and I'll send you the file!

Link to comment
Share on other sites

@ Aassdd - Thank you for the links and the time you spent to located them. I understand (a little anyway) about creating and using Progress bars. I'd like to figure out how to get a progress bar involved with another function that generally pauses the script, such as FileCopy().

I didn't test anything with the Do/Until idea you posted, but should the 'Until' syntax be : Until FileGetSize($Source) = FileGetSize($Destination) ? I would imagine it would only go through one cycle since the example you posted would always be true. If you assumed I'd understand this is what you meant anway, then I apologize heh. I'm still an AutoIT noob, unfortunately.

@ monoceres - Thanks so much for that small script. I tested it out and it seems to work great. I'll slap it into my test script to see what I can come up with then I'll throw it on here incase you'd like to take a look at it.

Thanks again, both of you, for your time and help.

Link to comment
Share on other sites

You could use something like this:

Func _FileCopyEx($s_File, $s_newFile)
    
    Local $SHFILEOPSTRUCT = DllStructCreate("hwnd hwnd;uint wFunc;ptr pFrom;ptr pTo;int fFlags;int fAnyOperationsAborted;hwnd hNameMappings;ptr lpszProgressTitle")
    
    Local $SHFILEOPSTRUCT_From = DllStructCreate("char pFrom[" & StringLen($s_File)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_From, "pFrom", $s_File)
    Local $SHFILEOPSTRUCT_To = DllStructCreate("char pTo[" & StringLen($s_newFile)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_To, "pTo", $s_newFile)
    
    DllStructSetData($SHFILEOPSTRUCT, "wFunc", 2) ; FO_COPY = 2
    DllStructSetData($SHFILEOPSTRUCT, "pFrom", DllStructGetPtr($SHFILEOPSTRUCT_From))
    DllStructSetData($SHFILEOPSTRUCT, "pTo", DllStructGetPtr($SHFILEOPSTRUCT_To))
    
    Local $i_State = DllCall("shell32.dll", "int", "SHFileOperation", "ptr", DllStructGetPtr($SHFILEOPSTRUCT))
    
    $SHFILEOPSTRUCT = 0
    
    If Not $i_State[0] Then
        SetError(1)
        Return 0
    EndIf
    
    SetError(0)
    Return 1
    
EndFuncoÝ÷ ÙKØî²Ùbëazz+©EW¢ºÚ"µÍÑ[PÛÜQ^
ØÜ    [È ][ÝÉÌLÛ^Y[K ][ÝËØÜ  [È ][ÝÉÌLÛ^Y[WØÛÜK  ][ÝÊ
Edited by FreeFry
Link to comment
Share on other sites

@ monoceres - Thanks so much for that small script. I tested it out and it seems to work great. I'll slap it into my test script to see what I can come up with then I'll throw it on here incase you'd like to take a look at it.

Just increase the chunksize to at least 128 kB otherwise copying will be painfully slow, and good luck!

Broken link? PM me and I'll send you the file!

Link to comment
Share on other sites

#Include <GUIConstants.au3>

$ThisApp = "Update Tool"

$App = GUICreate($ThisApp, 400, 150, @DesktopWidth / 2 - 200, @DesktopHeight / 2 - 100, $WS_POPUPWINDOW)
$Button_Update = GUICtrlCreateButton("Update", 50, 105, 75, 35)
$Button_Close = GUICtrlCreateButton("Close", 275, 105, 75, 35)
$Input_Status = GUICtrlCreateInput("Click update to download the latest version", 75, 45, 250, 20, $ES_CENTER + $ES_READONLY)
$Label_Speed = GUICtrlCreateLabel("Update Speed :", 25, 77)
$Input_Speed = GUICtrlCreateInput("", 105, 75, 70, 20, $ES_CENTER + $ES_READONLY)
$Label_Percent = GUICtrlCreateLabel(": % Complete", 300, 77)
$Input_Percent = GUICtrlCreateInput("", 225, 75, 70, 20, $ES_CENTER + $ES_READONLY)
$ProgressBar = GUICtrlCreateProgress(10, 10, 380, 20, $PBS_SMOOTH)

$SourceFile = "C:\Test\From\Spreadsheet.xls"
$DestFile = "C:\Test\To\Spreadsheet.xls"

Local $TimeToCopy, $DLSpeed, $TotalTime = 0
GUISetState()

While 1
    $msg = GUIGetMsg()
    If $msg = $Button_Close Then Exit
    If $msg = $Button_Update Then
        If FileExists($SourceFile) Then
            FileDelete($DestFile)
            
            $Source = FileOpen($SourceFile, 16); Open in read + binary mode
            $Destination = FileOpen($DestFile, 17); Open in write + binary mode
            $SourceSize = FileGetSize($SourceFile)
            $DataToRead =  $SourceSize * .01;50000; How many bytes the script will read at a time
            $DataCopied = 0
            $Data = "."
            $PercentComplete = 0
            
            GUICtrlSetState($Button_Update, $GUI_DISABLE)
            GUICtrlSetData($Input_Status, "Downloading Update  -  " & Int($SourceSize / 1000) & "KB")
            
            $TimeToCopy = TimerInit()
            
            While $Data <> ""
                If GUIGetMsg() = $Button_Close Then Abort()
                
                $Data = FileRead($Source, $DataToRead)
                If $Data <> "" Then FileWrite($Destination, $Data)
                $DataCopied += $DataToRead
                
                $TotalTime = Round(TimerDiff($TimeToCopy) / 1000, 2)
                $DLSpeed = Round(($DataCopied / Round(TimerDiff($TimeToCopy) / 1000, 2)) / 1000, 1)
                GUICtrlSetData($Input_Speed, $DLSpeed & "KBps")
                
                $PercentComplete = Int($DataCopied / $SourceSize * 100)
                GUICtrlSetData($ProgressBar, $PercentComplete)
                GUICtrlSetData($Input_Percent, $PercentComplete)
                
                Sleep(1)
                If $Data = "" Then ExitLoop
            WEnd
            FileClose($Source)
            FileClose($Destination)
            GUICtrlSetState($Button_Update, $GUI_ENABLE)
            GUICtrlSetData($Input_Status, "Update completed after " & $TotalTime & " seconds")
            ConsoleWrite($TotalTime & " seconds to update" & @CR)
            $TotalTime = 0
            $TimeToCopy = 0
        Else
            ConsoleWrite($SourceFile & " does not exist. Aborting" & @CR)
        EndIf
    EndIf
WEnd

Func Abort()
    MsgBox(262160, "Warning", "Update Aborted", 2)
    FileClose($Source)
    FileClose($Destination)
    FileDelete($DestFile)
    Exit
EndFunc ;==>Abort

$DataToRead = $SourceSize * .01 : I replaced your $chunksize variable with $DataToRead (To keep my brain from hurting too much). I set this so that I could at least see the progress bar movement, instead of instantly going from 1% to 100%, depending on file size. I doubt I would use this value in the actual program.

I tested it with many different file types and didn't experience corruption, so I'm quite happy with its performance.

The math I used to calculate download speed is inaccurate due to rounding, and 1000 bytes not really being 1KB. It was just another thing I thought I would try to play with and display to the user.

There was one oddity I found:

The original script that you posted does not care if the destination file already exists. I added the FileDelete before to get rid of it before any copying took place.

What I was finding was that if you ran the program once, you would successfully copy Spreadsheet.xls to the destination, yay. If you ran it again without deleting the old destination file, it would still run flawlessly, but then Windows would report the filesize to be double what it was before. If you ran it again, it would still run without errors, but now Windows would show the filesize to be triple the original size. I ran this 10 times on a 3 MB spreadsheet as a test, and it ended up being 30MB in the destination folder!

The good news is, once you actually open the file, it works properly as expected. When it is closed, Windows reports the original file size as 3MB. I'm on Win2k here at work, but I don't know if it is OS specific. Refreshing the view of the folder did not fix the filesize display, I had to actually open the file that I copied, then close it.

Thanks again for your help, Aassdd and monoceres. This really helped me a lot.

And FreeFry, I had already finished up this script before I came back to the forums, so give me some time and I'll see what I can do/learn from the code you posted. Though I must admit, I haven't touched any functions directly related to Dll's, so it may be a while =) But I do thank you for reading and posting.

Edited by Vakari
Link to comment
Share on other sites

You could use something like this:

Func _FileCopyEx($s_File, $s_newFile)
    
    Local $SHFILEOPSTRUCT = DllStructCreate("hwnd hwnd;uint wFunc;ptr pFrom;ptr pTo;int fFlags;int fAnyOperationsAborted;hwnd hNameMappings;ptr lpszProgressTitle")
    
    Local $SHFILEOPSTRUCT_From = DllStructCreate("char pFrom[" & StringLen($s_File)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_From, "pFrom", $s_File)
    Local $SHFILEOPSTRUCT_To = DllStructCreate("char pTo[" & StringLen($s_newFile)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_To, "pTo", $s_newFile)
    
    DllStructSetData($SHFILEOPSTRUCT, "wFunc", 2) ; FO_COPY = 2
    DllStructSetData($SHFILEOPSTRUCT, "pFrom", DllStructGetPtr($SHFILEOPSTRUCT_From))
    DllStructSetData($SHFILEOPSTRUCT, "pTo", DllStructGetPtr($SHFILEOPSTRUCT_To))
    
    Local $i_State = DllCall("shell32.dll", "int", "SHFileOperation", "ptr", DllStructGetPtr($SHFILEOPSTRUCT))
    
    $SHFILEOPSTRUCT = 0
    
    If Not $i_State[0] Then
        SetError(1)
        Return 0
    EndIf
    
    SetError(0)
    Return 1
    
EndFuncoÝ÷ ÙKØî²Ùbëazz+©EW¢ºÚ"µÍÑ[PÛÜQ^
ØÜ    [È ][ÝÉÌLÛ^Y[K ][ÝËØÜ  [È ][ÝÉÌLÛ^Y[WØÛÜK  ][ÝÊ
This is wonderful, Is there anyway to make it overwrite the file without asking for permission
Link to comment
Share on other sites

This is wonderful, Is there anyway to make it overwrite the file without asking for permission

Yep there is, it requires just a little modification to the function(I haven't bothered adding a simple parameter for it yet, as this win api function takes alot of parameters..):

Func _FileCopyEx($s_File, $s_newFile)
    
    Local $SHFILEOPSTRUCT = DllStructCreate("hwnd hwnd;uint wFunc;ptr pFrom;ptr pTo;int fFlags;int fAnyOperationsAborted;hwnd hNameMappings;ptr lpszProgressTitle")
    
    Local $SHFILEOPSTRUCT_From = DllStructCreate("char pFrom[" & StringLen($s_File)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_From, "pFrom", $s_File)
    Local $SHFILEOPSTRUCT_To = DllStructCreate("char pTo[" & StringLen($s_newFile)+2 & "]")
    DllStructSetData($SHFILEOPSTRUCT_To, "pTo", $s_newFile)
    
    DllStructSetData($SHFILEOPSTRUCT, "wFunc", 2) ; FO_COPY = 2
    DllStructSetData($SHFILEOPSTRUCT, "pFrom", DllStructGetPtr($SHFILEOPSTRUCT_From))
    DllStructSetData($SHFILEOPSTRUCT, "pTo", DllStructGetPtr($SHFILEOPSTRUCT_To))
    DllStructSetData($SHFILEOPSTRUCT, "fFlags", 16)         ; <---- Added
    
    Local $i_State = DllCall("shell32.dll", "int", "SHFileOperation", "ptr", DllStructGetPtr($SHFILEOPSTRUCT))
    
    $SHFILEOPSTRUCT = 0
    
    If Not $i_State[0] Then
        SetError(1)
        Return 0
    EndIf
    
    SetError(0)
    Return 1
    
EndFunc

That will answer yes to any dialogs that would pop up. :)

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...