Page 3 of 4

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 2:48 pm
by Marc56us
By doing a first simulation run, Robocopy displays the number of files that will be copied, updated or deleted.
Like this:

Code: Select all

               Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs :       373         0       373         0         0        10
   Files :      3223        10      3213         0         0       126
Simply uses this value as the final value of the progress bar.

To simulate copy, use normal parameter and add /L

(Robocopy /? to see all parameters in your language)

8)

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 2:53 pm
by gonpublic2k
Marc56us wrote:By doing a first simulation run, Robocopy displays the number of files that will be copied, updated or deleted.
Like this:

Code: Select all

               Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs :       373         0       373         0         0        10
   Files :      3223        10      3213         0         0       126
Simply uses this value as the final value of the progress bar.

To simulate copy, use normal parameter and add /L

(Robocopy /? to see all parameters in your language)

8)
Good point.

For now, this is what I have so far from what I gathered in the forums. This is ok to display a PB Windows that
shows ROBOCOPY running, the problem is that it stalls the program in the middle of the copy process and it
'hangs' until you have to kill it. When I check the destination all the files were copied but you have no way of
knowing what's going on and the program has to be killed.

Code: Select all

Source$  =  "c:\users\computos\desktop\SampleTest"
Destination$  =   "z:\linux"

OpenWindow(0, 320, 320, 422, 250, "File Opeations - ROBOCOPY", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

EditorGadget(0, 8, 8, 406, 233)

DOS = RunProgram("robocopy.exe", "/S /E /MT:32 " + Source$ + " " + Destination$, "", #PB_Program_Hide | #PB_Program_Open | #PB_Program_Read)

If DOS
  Output$ = ""
  While ProgramRunning(DOS)
    If AvailableProgramOutput(DOS)
      Output$ = ReadProgramString(DOS)
    AddGadgetItem(0, -1, Output$)
    UpdateWindow_(GadgetID(0))
  EndIf
  Wend
  CloseProgram(DOS)
EndIf

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 3:06 pm
by Marc56us
1. Start by using Robocopy without threads (remove /MT:32)
2. Avoid using APIs when an equivalent function exists in the language
UpdateWindow_(GadgetID(0)) :arrow: While WindowEvent : Wend
3. Removes the display of the percentage of progress (Add /NP)
4. Duplicates the output to a log file (/TEE /unilog:Backup.txt)
5. I tested your code with files of my own: it works, so you may have a file that blocks (hence the interest of the log). See the options for passing problem files.

For the rest of GUI use

PathRequester() to select directory
or
ExplorerListGadget() with #PB_Explorer_MultiSelect to select multiple directory/files

You're on the good way, continue

8)

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 3:33 pm
by gonpublic2k
Marc56us wrote:1. Start by using Robocopy without threads (remove /MT:32)
2. Avoid using APIs when an equivalent function exists in the language
UpdateWindow_(GadgetID(0)) :arrow: While WindowEvent : Wend
3. Removes the display of the percentage of progress (Add /NP)
4. Duplicates the output to a log file (/TEE /unilog:Backup.txt)
5. I tested your code with files of my own: it works, so you may have a file that blocks (hence the interest of the log). See the options for passing problem files.

For the rest of GUI use

PathRequester() to select directory
or
ExplorerListGadget() with #PB_Explorer_MultiSelect to select multiple directory/files

You're on the good way, continue

8)
Excellent, I should follow your suggestions and report back with the progress. I appreciate it. :D

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 3:56 pm
by gonpublic2k
gonpublic2k wrote:
Marc56us wrote:1. Start by using Robocopy without threads (remove /MT:32)
2. Avoid using APIs when an equivalent function exists in the language
UpdateWindow_(GadgetID(0)) :arrow: While WindowEvent : Wend
3. Removes the display of the percentage of progress (Add /NP)
4. Duplicates the output to a log file (/TEE /unilog:Backup.txt)
5. I tested your code with files of my own: it works, so you may have a file that blocks (hence the interest of the log). See the options for passing problem files.

For the rest of GUI use

PathRequester() to select directory
or
ExplorerListGadget() with #PB_Explorer_MultiSelect to select multiple directory/files

You're on the good way, continue


This part I didn't understand - please clarify or help me with a sample code snippet demonstrating
what you mean:
2. Avoid using APIs when an equivalent function exists in the language
UpdateWindow_(GadgetID(0)) :arrow: While WindowEvent : Wend
8)
Excellent, I should follow your suggestions and report back with the progress. I appreciate it. :D

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 4:13 pm
by Marc56us
This part I didn't understand - please clarify or help me with a sample code snippet demonstrating
what you mean:
2. Avoid using APIs when an equivalent function exists in the language
UpdateWindow_(GadgetID(0)) :arrow: While WindowEvent : Wend
Gadgets (such as lists) are only updated by the program only during a user intervention (e. g. a mouse click) or by certain events.
When a list is updated quickly, you don't see the elements displayed at each loop, but sometimes only at the end. As a result, you sometimes think that the program is crashed.

UpdateWindow_(GadgetID(0)) is a Windows API, it works, but often using API directly adds a factor of doubt to antivirus software. That is why we used only when there is nothing equivalent in the basic language.

The double instruction While WindowEvent : Wend is a trick (given by Fred himself) that allows to inject a false user action (of a single iteration) and therefore very fast. The list is therefore automatically updated.

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 4:19 pm
by infratec
If you want to program window programs,
you have to understand how they work.

They are event driven.
Each things generates events which have to be handled,

For that you have ONE event loop in your program.
While this loop is running you get everything refreshed and updated.

If not, then you will miss events and your program is not 'responsive'.

If you read back in my posts, you will find the point where I wrote that your copy stuff has to be in a thread,
that you can update the GUI elements.

This is all programming language independent.

And I have also written that you have to cllect both stdout AND stderr.
Up to now you only catch stdout.

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 5:20 pm
by gonpublic2k
infratec wrote:If you want to program window programs,
you have to understand how they work.

They are event driven.
Each things generates events which have to be handled,

For that you have ONE event loop in your program.
While this loop is running you get everything refreshed and updated.

If not, then you will miss events and your program is not 'responsive'.

If you read back in my posts, you will find the point where I wrote that your copy stuff has to be in a thread,
that you can update the GUI elements.

This is all programming language independent.

And I have also written that you have to cllect both stdout AND stderr.
Up to now you only catch stdout.
Remember I'm starting to use PB now. I'm still a newbie in most of the concepts of programming in this language, although I have experience
programming in other languages it's still a little learning curve to grasp all the concepts. I appreciate the comments and the help however but
bear with me. Baby steps :D, in the meantime -- I'm putting together the code with little snippets and suggestions I find around the forum, so
if you want to show me something, please include a sample to illustrate, is easier for me to see it in the code than you telling me "use this function
or do it this way" , that's just how I work. Sorry to be a pain in the ass but I need all the help I can get :D :D !!

Thanks!!

Re: CopyFileEx API call

Posted: Tue Nov 19, 2019 6:06 pm
by infratec

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 2:26 pm
by gonpublic2k

Hello once again! Following your code skeleton, I tried running the project with the example and adapting it
to ROBOCOPY. I'm not sure if it's an error somewhere but it doesn't display anything, it simply starts and
really quickly quits. Please shed some light, thanks!

Code: Select all

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

CompilerIf Not #PB_Compiler_Thread
  MessageRequester("Info", "Enable Thread-Safe in compiler options!")
  End
CompilerEndIf


Enumeration #PB_Event_FirstCustomValue
  #ExternalProgram_Event_StdOut
  #ExternalProgram_Event_Error
  #ExternalProgram_Event_Exit
EndEnumeration


Structure ExternalProgramParameterStructure
  Program$
  ProgramParameter$
  ProgramWorkingDirectory$
  ProgramReadWriteMode.i
  ProgramExit$
  Semaphore.i
  Mutex.i
  Thread.i
  ProgramID.i
  StdOut$
  StdIn$
  StdErr$
  Exit.i
  
EndStructure




Procedure ExternalProgramThread(*Parameter.ExternalProgramParameterStructure)
 
  Protected *Buffer, ReadLen.i, WriteLen.i, Error$, Timeout.i
 
  *Buffer = AllocateMemory(1024)
 
  If *Buffer
   
    *Parameter\ProgramID = RunProgram(*Parameter\Program$, *Parameter\ProgramParameter$, *Parameter\ProgramWorkingDirectory$, #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_Hide|*Parameter\ProgramReadWriteMode)
   
    If *Parameter\ProgramID
     
      Repeat
       
        ReadLen = AvailableProgramOutput(*Parameter\ProgramID)
        If ReadLen
          ReadLen = ReadProgramData(*Parameter\ProgramID, *Buffer, ReadLen)
          If ReadLen
            *Parameter\StdOut$ = PeekS(*Buffer, ReadLen, *Parameter\ProgramReadWriteMode|#PB_ByteLength)
            PostEvent(#ExternalProgram_Event_StdOut)
            WaitSemaphore(*Parameter\Semaphore)
          EndIf
        EndIf
       
        Error$ = ReadProgramError(*Parameter\ProgramID, *Parameter\ProgramReadWriteMode)
        If Len(Error$)
          *Parameter\StdErr$ = Error$
          PostEvent(#ExternalProgram_Event_Error)
          WaitSemaphore(*Parameter\Semaphore)
        EndIf
       
        If TryLockMutex(*Parameter\Mutex)
          If Len(*Parameter\StdIn$)
            *Parameter\StdIn$ + #CRLF$
            WriteLen = PokeS(*Buffer, *Parameter\StdIn$, -1, *Parameter\ProgramReadWriteMode)
            *Parameter\StdIn$ = ""
            WriteLen = WriteProgramData(*Parameter\ProgramID, *Buffer, WriteLen)
            SignalSemaphore(*Parameter\Semaphore)
          EndIf
          UnlockMutex(*Parameter\Mutex)
        EndIf
       
        Delay(10)
       
      Until *Parameter\Exit Or Not ProgramRunning(*Parameter\ProgramID)
     
      If *Parameter\Exit
        If ProgramRunning(*Parameter\ProgramID)
          If Len(*Parameter\ProgramExit$)
            *Parameter\ProgramExit$ + #CRLF$
            WriteLen = PokeS(*Buffer, *Parameter\ProgramExit$, -1, *Parameter\ProgramReadWriteMode)
            WriteProgramData(*Parameter\ProgramID, *Buffer, WriteLen)
          Else
            WriteProgramData(*Parameter\ProgramID, #PB_Program_Eof, 0)
          EndIf
          Timeout = 300
          Repeat
            Delay(10)
            Timeout - 1
          Until Timeout = 0 Or Not ProgramRunning(*Parameter\ProgramID)
          If Timeout = 0
            Debug "Kill"
            KillProgram(*Parameter\ProgramID)
          EndIf
        EndIf
      EndIf
     
      CloseProgram(*Parameter\ProgramID)
     
    EndIf
   
    FreeMemory(*Buffer)
  EndIf
 
  PostEvent(#ExternalProgram_Event_Exit)
 
EndProcedure


;- Example
CompilerIf #PB_Compiler_IsMainFile
 
  ; you can download phantomjs here:
  ; http://phantomjs.org/download.html
 
  Enumeration
    #Form0
    #EditorCommand
    #StringCommand
    #Button
  EndEnumeration
 
 
 
  Define Event.i, Exit.i, i.i, j.i
  Define ThreadParameter.ExternalProgramParameterStructure
  
  Define Source$, Destination$
  
  Source$       = "c:\users\computos\desktop\SampleTest\"
  Destination$  = "z:\linux\"
 
 
  OpenWindow(#Form0, 379, 176, 608, 542, "Backup User Profile", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_TitleBar)
  EditorGadget(#EditorCommand, 5, 15, 593, 447)
  StringGadget(#StringCommand, 6, 469, 594, 23, [color=#FF0080]"/S /E /TEE /log:backup.log "+ Source$ + " " + Destination$)[/color]
  ButtonGadget(#Button, 211, 503, 201, 33, "Begin Backup!")
 
  ThreadParameter\Program$ = "robocopy.exe"
  ThreadParameter\ProgramReadWriteMode = #PB_UTF8
  ThreadParameter\ProgramExit$ = "Break"
  ThreadParameter\Semaphore = CreateSemaphore()
  ThreadParameter\Mutex = CreateMutex()
  ThreadParameter\Thread = CreateThread(@ExternalProgramThread(), @ThreadParameter)
 
  Repeat
   
    Event = WaitWindowEvent()
   
    Select Event
      Case #ExternalProgram_Event_StdOut
        ThreadParameter\StdOut$ = RemoveString(ThreadParameter\StdOut$, #CR$)
        ThreadParameter\StdOut$ = RTrim(ThreadParameter\StdOut$, #LF$)
        i = CountString(ThreadParameter\StdOut$, #LF$)
        For j = 0 To i
          AddGadgetItem(#EditorCommand, -1, StringField(ThreadParameter\StdOut$, j + 1, #LF$))
        Next j
        SignalSemaphore(ThreadParameter\Semaphore)
       
      Case #ExternalProgram_Event_Error
        Debug ThreadParameter\StdErr$
        SignalSemaphore(ThreadParameter\Semaphore)
       
      Case #ExternalProgram_Event_Exit
        Exit = #True
       
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #Button
            LockMutex(ThreadParameter\Mutex)
            ThreadParameter\StdIn$ = GetGadgetText(#StringCommand)
            UnlockMutex(ThreadParameter\Mutex)
            WaitSemaphore(ThreadParameter\Semaphore)
        EndSelect
       
      Case #PB_Event_CloseWindow
        If IsThread(ThreadParameter\Thread)
          ThreadParameter\Exit = #True
          WaitThread(ThreadParameter\Thread)
        EndIf
        FreeMutex(ThreadParameter\Mutex)
        FreeSemaphore(ThreadParameter\Semaphore)
        Exit = #True
    EndSelect
   
  Until Exit
   
CompilerEndIf

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 2:31 pm
by Marc56us
(I just saw your post so I haven't read it yet. I had prepared this this morning. It's up to you if you're interested)

Since you are just starting out, here is a base that also shows some possibilities of PB
- Management of program parameters
- PathRequester() for the user to choose if no parameters have been passed to the program
- Subprogram operation in Thread mode
- Possibility of stopping the Thread (no panic, this type of thread does not share a memory area for writing and can therefore be stopped without any problem)
- Counting of files and folders to be processed
- Main loop management
- Resizing the interface automatically

Note: All the interface has been done with the Form Designer and the copied/pasted code for easy reading but you can put it back in.pbf to edit.

The code does not use any direct APIs or 'corrosive' or complicated functions (ie: Poke or pointer usage) and should therefore not be quarantined by good antivirus software.

Code: Select all

EnableExplicit

CompilerIf Not #PB_Compiler_Thread
  MessageRequester("Info", "Enable Thread-Safe in compiler options!")
  End
CompilerEndIf

; By default all gadgets will use proportional font (Consolas 9)
SetGadgetFont(#PB_Default, FontID(LoadFont(#PB_Any, "Consolas", 9)))

; Load GUI form from file
; XIncludeFile "RoboCopy_GUI.pbf" 
; To simplify here manualy insert
; --- Start Form -----------------------------------------------------------------
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;

Enumeration FormWindow
  #Window_0
EndEnumeration

Enumeration FormGadget
  #Btn_Verify
  #Btn_Quit
  #ListView_0
  #Btn_Stop
  #Btn_Run
EndEnumeration

Declare ResizeGadgetsWindow_0()


Procedure OpenWindow_0(x = 0, y = 0, width = 600, height = 400)
  OpenWindow(#Window_0, x, y, width, height, "RoboCopy PureGUI", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
  CreateStatusBar(0, WindowID(#Window_0))
  AddStatusBarField(350)
  StatusBarText(0, 0, "Ready")
  AddStatusBarField(#PB_Ignore)
  StatusBarProgress(0, 1, 0)
  ButtonGadget(#Btn_Verify, 10, 10, 100, 25, "Verify")
  ButtonGadget(#Btn_Quit, 490, 10, 100, 25, "Quit")
  ListViewGadget(#ListView_0, 10, 40, 580, 330)
  ButtonGadget(#Btn_Stop, 120, 10, 100, 25, "Stop")
  DisableGadget(#Btn_Stop, 1)
  ButtonGadget(#Btn_Run, 230, 10, 100, 25, "Run")
  DisableGadget(#Btn_Run, 1)
EndProcedure

Procedure ResizeGadgetsWindow_0()
  Protected FormWindowWidth, FormWindowHeight
  FormWindowWidth = WindowWidth(#Window_0)
  FormWindowHeight = WindowHeight(#Window_0)
  ResizeGadget(#Btn_Quit, FormWindowWidth - 110, 10, 100, 25)
  ResizeGadget(#ListView_0, 10, 40, FormWindowWidth - 20, FormWindowHeight - StatusBarHeight(0) - 47)
EndProcedure
; --- end of Form --------------------------------------------------------------------

; Source and destination passed by parameters (add in compiler option)
Global Source$      = ProgramParameter(0)
Global Destination$ = ProgramParameter(1)

; If not, ask user
If CountProgramParameters() <> 2
    Source$      = PathRequester("Source", "")
    Destination$ = PathRequester("Destination", "")
EndIf

Global Parameters$  = " /L " + Source$ + " " + Destination$
Global ID_Thread_List, ID_Thread_Run
Global Files_Folders

; Application Start Here
OpenWindow_0()
StatusBarText(0, 0, "Source: " + Source$ + " - Destination: " + Destination$)



; Procedure must be declared in order or use Declare
Procedure Robocopy_List_Only(*Value)
    Protected Run = RunProgram("robocopy",  Parameters$, "", 
                               #PB_Program_Open|#PB_Program_Read|#PB_Program_Error|#PB_Program_Hide)
    If Run
        DisableGadget(#Btn_Stop, 0)
        StatusBarText(0, 0, "Please Wait...")
        While ProgramRunning(Run)
            If AvailableProgramOutput(Run) 
                Protected stdout$ = ReadProgramString(Run)
                AddGadgetItem(#ListView_0, -1, stdout$)    
                Files_Folders + 1
                Protected stderr$ = ReadProgramError(Run, #PB_Ascii)
                If stderr$ 
                    StatusBarText(0, 0, "ERROR: " + stderr$)
                EndIf
            EndIf
        Wend
        DisableGadget(#Btn_Stop, 1)
        StatusBarText(0, 0, "Done.") 
        
        ; Statistics (need to know how may file and folder for progressbar)
        Files_Folders - 26 ; = Nb lines - header and resume
        StatusBarText(0, 0, "Files & Folders : " + Str(Files_Folders))    
        ; Activate Run button
        DisableGadget(#Btn_Run, 0)
    EndIf    
EndProcedure



Procedure Robocopy_Run(*value)
    ; Coming soon...
    ; Same as Robocopy_List_Only(*Value)
    ; Without /L parameter
    ; With ProgressBar (use Files_Folders as max value)
    ; To change progressbar, count lines here and substract from 'Files_Folders'
    MessageRequester("Coming Soon", "This function is not yet implemented", 
                     #PB_MessageRequester_Error)
EndProcedure



; Main loop
Repeat 
    Select WaitWindowEvent()     
            
        Case #PB_Event_Gadget   
            Select EventGadget()
                Case #Btn_Quit
                    End
                Case #Btn_Verify
                    ID_Thread_List = CreateThread(@Robocopy_List_Only(), 1)
                Case #Btn_Run
                    ID_Thread_Run  = CreateThread(@Robocopy_Run(), 2)
                Case #Btn_Stop
                    If IsThread(ID_Thread_List) : KillThread(ID_Thread_List) : EndIf
                    If IsThread(ID_Thread_Run)  : KillThread(ID_Thread_Run)  : EndIf
            EndSelect       
            
        Case #PB_Event_CloseWindow     
            End    
            
        Case #PB_Event_SizeWindow      
            ResizeGadgetsWindow_0()
            
    EndSelect
ForEver

End
Enjoy 8)

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 2:44 pm
by gonpublic2k
Marc56us

Wow!! Simply incredible! I appreciate this so much, looks very interesting, I'm going to follow your advice and try to implement
the 'RUN' procedure to include the parameters for the copy of files. This is really a great piece of code, I can't thank you enough
for this!!!

:D

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 3:45 pm
by Marc56us
:idea: Since the two procedures are almost identical, only one procedure can be used and the variable passed as a parameter can be used to differentiate between actions

TEST Mode
- Add /L to the initial parameter line
- Count the lines

RUN mode
- Delete /L from the parameter line
- Advance the progress bar (Using the number of lines counted by the test)

Code: Select all

EnableExplicit

CompilerIf Not #PB_Compiler_Thread
    MessageRequester("Info", "Enable Thread-Safe in compiler options!")
    End
CompilerEndIf

; By default all gadgets will use proportional font (Consolas 9)
SetGadgetFont(#PB_Default, FontID(LoadFont(#PB_Any, "Consolas", 9)))

; Load GUI form
; XIncludeFile "RoboCopy_GUI.pbf" 
; -----------------------------------------------------------------------------------
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;

Enumeration FormWindow
    #Window_0
EndEnumeration

Enumeration FormGadget
    #Btn_Verify
    #Btn_Quit
    #ListView_0
    #Btn_Stop
    #Btn_Run
    #Btn_Clear
EndEnumeration

Declare ResizeGadgetsWindow_0()


Procedure OpenWindow_0(x = 0, y = 0, width = 600, height = 400)
    OpenWindow(#Window_0, x, y, width, height, "RoboCopy PureGUI", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
    CreateStatusBar(0, WindowID(#Window_0))
    AddStatusBarField(350)
    StatusBarText(0, 0, "Ready")
    AddStatusBarField(#PB_Ignore)
    StatusBarProgress(0, 1, 0)
    ButtonGadget(#Btn_Verify, 10, 10, 100, 25, "Verify")
    ButtonGadget(#Btn_Quit, 490, 10, 100, 25, "Quit")
    ListViewGadget(#ListView_0, 10, 40, 580, 330)
    ButtonGadget(#Btn_Stop, 120, 10, 100, 25, "Stop")
    DisableGadget(#Btn_Stop, 1)
    ButtonGadget(#Btn_Run, 230, 10, 100, 25, "Run")
    DisableGadget(#Btn_Run, 1)
    ButtonGadget(#Btn_Clear, 340, 10, 100, 25, "Clear Report")
EndProcedure

Procedure ResizeGadgetsWindow_0()
    Protected FormWindowWidth, FormWindowHeight
    FormWindowWidth = WindowWidth(#Window_0)
    FormWindowHeight = WindowHeight(#Window_0)
    ResizeGadget(#Btn_Quit, FormWindowWidth - 110, 10, 100, 25)
    ResizeGadget(#ListView_0, 10, 40, FormWindowWidth - 20, FormWindowHeight - StatusBarHeight(0) - 47)
EndProcedure


; -----------------------------------------------------------------------------------

; Source and destination passed by parameters (add in compiler option)
Global Source$      = ProgramParameter(0)
Global Destination$ = ProgramParameter(1)

; If not, ask user
If CountProgramParameters() <> 2
    Source$      = PathRequester("Source", "")
    Destination$ = PathRequester("Destination", "")
EndIf

Global Parameters$  = " /S " + Source$ + " " + Destination$
Global ID_Thread_List, ID_Thread_Run
Global Files_Folders

; Application Start Here
OpenWindow_0()
StatusBarText(0, 0, "Source: " + Source$ + " - Destination: " + Destination$)


Procedure RoboCopy_Do(*Value)
    If *Value = 1 : Debug "Testing" 
        Files_Folders = 0
        Parameters$  + " /L"   
        Debug Parameters$
    EndIf
    
    If *Value = 2 : Debug "Run"
        Files_Folders - 26 ; = Nb lines - header and resume
        Parameters$ = RemoveString(Parameters$, " /L")
        Debug Parameters$
    EndIf
    
    Protected Run = RunProgram("robocopy",  Parameters$, "", 
                               #PB_Program_Open|#PB_Program_Read|#PB_Program_Error|#PB_Program_Hide)
    If Run
        Protected i
        DisableGadget(#Btn_Stop, 0)
        StatusBarText(0, 0, "Please Wait...")
        While ProgramRunning(Run)
            If AvailableProgramOutput(Run) 
                Protected stdout$ = ReadProgramString(Run)
                AddGadgetItem(#ListView_0, -1, stdout$)  
                
                If *Value = 1 
                    Files_Folders + 1
                EndIf
                
                If *Value = 2 
                    i + 1
                    StatusBarProgress(0, 1, i, #PB_StatusBar_Raised, 0, Files_Folders) 
                EndIf      
                
                Protected stderr$ = ReadProgramError(Run, #PB_Ascii)
                If stderr$ 
                    StatusBarText(0, 0, "ERROR: " + stderr$)
                EndIf
            EndIf
        Wend
        DisableGadget(#Btn_Stop, 1)
        StatusBarText(0, 0, "Done.") 
        ; Activate Run button
        DisableGadget(#Btn_Run, 0)
        If *Value = 1 : StatusBarText(0, 0, "Files & Folders : " + Str(Files_Folders)) : EndIf 
        ; Fill 100% (if progressbar not full updated)
        If *Value = 2 : StatusBarProgress(0, 1, Files_Folders) : EndIf
    EndIf  
EndProcedure



; Main loop
Repeat 
    Select WaitWindowEvent()     
            
        Case #PB_Event_Gadget   
            Select EventGadget()
                Case #Btn_Quit
                    End
                Case #Btn_Verify
                    ID_Thread_List = CreateThread(@RoboCopy_Do(), 1)
                Case #Btn_Run
                    ID_Thread_List = CreateThread(@RoboCopy_Do(), 2)
                Case #Btn_Stop
                    If IsThread(ID_Thread_List) : KillThread(ID_Thread_List) : EndIf
                Case #Btn_Clear
                    ClearGadgetItems(#ListView_0)
            EndSelect       
            
        Case #PB_Event_CloseWindow     
            End    
            
        Case #PB_Event_SizeWindow      
            ResizeGadgetsWindow_0()
            
    EndSelect
ForEver

End
Very aerated clean for readability.

Use CTRL+A followed by CTRL+I often, this restores indentation and allows you to quickly see nesting errors or loops that are not properly closed.

There is still work to be done:
- Check boxes or options for the user
- Check the parameters
- Create a log file (Robocopy has an option for this (/TEE /unilog:Backup.txt) that allows you to duplicate the output.
- Add a TimeStamp to the log file so that it is not deleted every time
- Make an "About" box with the credits (PureBasic.com, Robocopy.exe (MS)) and a very nice PureBasic logo!

"Think Simple"

Enjoy 8)

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 4:08 pm
by gonpublic2k
Awesome!!!!

Thanks!!!!!! :D :D :D :D :D

Re: CopyFileEx API call

Posted: Wed Nov 20, 2019 6:57 pm
by gonpublic2k
Very aerated clean for readability.

Use CTRL+A followed by CTRL+I often, this restores indentation and allows you to quickly see nesting errors or loops that are not properly closed.

There is still work to be done:
- Check boxes or options for the user
- Check the parameters
- Create a log file (Robocopy has an option for this (/TEE /unilog:Backup.txt) that allows you to duplicate the output.
- Add a TimeStamp to the log file so that it is not deleted every time
- Make an "About" box with the credits (PureBasic.com, Robocopy.exe (MS)) and a very nice PureBasic logo!
I'd also like to add the following:

- Automatic scrolling of the copy process in the list Window
- Display a percentage indicator (label) next to the progress bar in the status bar
- Ability to save the backup log file in the remote path (Destination)

:D