Jewtus

Task Schedule folder sync

4 posts in this topic

I'm about to start building a sync function that needs to do the following:

1) Have an interface that allows you to select folders and sub folders that you want to back up

2) Remember (even after closing) the selected folders

3) Create a task in task scheduler that will perform the backup function

4) The original interface needs to be able to see the old configurations and also update them.

 

This was the approach I was going to take and I was looking to see if anyone has already built something like this that I can adapt or if anyone has a better idea on how I can do this.

The GUI that will allow for folder selection I have not even looked into yet, but I'm pretty sure there is a sample script in Tree view that has check boxes. I was going to use that to record the folders that are going to be backed up and save the list to a txt file. Then I was going to use an xcopy script to back up the folders to a specified network location using the data in the previously created txt file to drive the xcopy function. Then I was going to create an xml output that could be imported to task scheduler.

 

Anyone have suggestions on the GUI or the task scheduler? The xcopy function is pretty strait forward.

Share this post


Link to post
Share on other sites



Update:

I figured out how to do most of this, and here is my script as is right now

#include <GUIConstantsEx.au3>
#include <TreeViewConstants.au3>
#include <WindowsConstants.au3>
#Include <GuiTreeView.au3>
#Include <File.au3>
#Include <GuiListView.au3>

$sNetbackup="H:\Backup"
If FileExists($sNetbackup) = 0 Then DirCreate($sNetbackup)
$sScriptDir=@UserProfileDir&"\Scripts\"
If FileExists($sScriptDir) = 0 Then DirCreate($sScriptDir)

$gui = GUICreate("File Browser", 756, 272, -1, -1)
$iTree = GUICtrlCreateTreeView(0, 0, 361, 233, $GUI_SS_DEFAULT_TREEVIEW)
$cButton = GUICtrlCreateButton("Save", 672, 240, 75, 25)
$cAddSelected = GUICtrlCreateButton(">", 368, 40, 35, 25)
$cRemoveSelected = GUICtrlCreateButton("<", 368, 72, 35, 25)
$cRemoveAll = GUICtrlCreateButton("<<", 368, 104, 35, 25)
$cPathList = GUICtrlCreateListView("", 408, 0, 345, 233,0x0003)
_GUICtrlListView_AddColumn($cPathList,"Folder",340)
;Get old folders
$aImportFolders=_CSVSplit(FileRead($sScriptDir&"Folders.txt"),",")
For $p=0 to UBound($aImportFolders)-1
    _GUICtrlListView_AddItem($cPathList,$aImportFolders[$p])
Next

$hImage = _GUIImageList_Create(16, 16, 5, 2)
_GUIImageList_AddIcon($hImage, @SystemDir & "\shell32.dll", 4)
_GUIImageList_AddIcon($hImage, @SystemDir & "\shell32.dll", 54)
_GUICtrlTreeView_SetNormalImageList($iTree, $hImage)
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
$root = _GUICtrlTreeView_AddChild($iTree,"","C:",0)
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $cRemoveSelected
            _GUICtrlListView_DeleteItemsSelected($cPathList)
        Case $cRemoveAll
            _GUICtrlListView_DeleteAllItems($cPathList)
        Case $cAddSelected
            $sPath=GetSelectedPath($iTree)
            _GUICtrlListView_AddItem($cPathList,$sPath)
        Case $cButton
            $aFolders=_GUICtrlListView_CreateArray($cPathList)
            FileDelete($sScriptDir&"AutoBackup.bat")
            FileDelete($sScriptDir&"AutoBackup.xml")
            FileDelete($sScriptDir&"Folders.txt")
            For $z=1 to UBound($aFolders)-1
                $splitter=StringSplit($aFolders[$z][0],"\")
                $sfoldername=$splitter[UBound($splitter)-1]
                FileWriteLine($sScriptDir&"AutoBackup.bat",'xcopy "'&$aFolders[$z][0]&'\*" "H:\Backup\'&$sfoldername&'" /S /I')
                FileWriteLine($sScriptDir&"Folders.txt",$aFolders[$z][0])
            Next
            $sXMLPath=$sScriptDir&"AutoBackup.xml"
            FileWriteLine($sXMLPath,'<?xml version="1.0" encoding="UTF-16"?>'&@CRLF&'<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">'&@CRLF&'  <Triggers>'&@CRLF&'    <CalendarTrigger>'&@CRLF&'      <StartBoundary>'&@YEAR&'-'&@MON&'-'&@MDAY&'T00:00:00</StartBoundary>'&@CRLF&'      <Enabled>true</Enabled>'&@CRLF&'      <ScheduleByDay>'&@CRLF&'        <DaysInterval>1</DaysInterval>'&@CRLF&'      </ScheduleByDay>'&@CRLF&'    </CalendarTrigger>'&@CRLF&'  </Triggers>'&@CRLF&'  <Settings>'&@CRLF&'    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>'&@CRLF&'    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>'&@CRLF&'    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>'&@CRLF&'    <AllowHardTerminate>true</AllowHardTerminate>'&@CRLF&'    <StartWhenAvailable>true</StartWhenAvailable>'&@CRLF&'    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>'&@CRLF&'    <IdleSettings>'&@CRLF&'      <StopOnIdleEnd>true</StopOnIdleEnd>'&@CRLF&'      <RestartOnIdle>false</RestartOnIdle>'&@CRLF&'    </IdleSettings>'&@CRLF&'    <AllowStartOnDemand>true</AllowStartOnDemand>'&@CRLF&'    <Enabled>true</Enabled>'&@CRLF&'    <Hidden>false</Hidden>'&@CRLF&'    <RunOnlyIfIdle>false</RunOnlyIfIdle>'&@CRLF&'    <WakeToRun>false</WakeToRun>'&@CRLF&'    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>'&@CRLF&'    <Priority>7</Priority>'&@CRLF&'  </Settings>'&@CRLF&'  <Actions Context="Author">'&@CRLF&'    <Exec>'&@CRLF&'      <Command>"'&$sScriptDir&'AutoBackup.bat"</Command>'&@CRLF&'    </Exec>'&@CRLF&'  </Actions>'&@CRLF&'</Task>')
            $aBreakdownName=StringSplit($sXMLPath,"\")
            If UBound($aImportFolders)=0 then ; Check to see if a previous folder was created
                RunWait('schtasks /Create /XML "'&$sXMLPath&'" /TN "'&StringTrimRight($aBreakdownName[UBound($aBreakdownName)-1],4)&'"',@UserProfileDir,@SW_HIDE)
                Run("control schedtasks",@UserProfileDir,@SW_HIDE)
                GUIDelete($gui)
                Do
                    Sleep(100)
                Until WinExists('Task Scheduler')
                MsgBox(0,"Process Complete","New task has been created. Click the 'Task Scheduler Library' to make changes to the saved task 'AutoBackup'.")
            Else
                GUIDelete($gui)
                MsgBox(0,"Process Complete","The saved task has been modified.")
            EndIf
            Exit
    EndSwitch
WEnd

Func GetSelectedPath($iTree)
    $hWndTreeView = GUICtrlGetHandle($iTree)
    $item=_GUICtrlTreeView_GetSelection($hWndTreeView)
    $txt = _GUICtrlTreeView_GetText($hWndTreeView,$item)
    Do
        $parent = _GUICtrlTreeView_GetParentHandle($hWndTreeView,$item)
        If $parent <> 0 Then
            $txt = _GUICtrlTreeView_GetText($hWndTreeView,$parent) & "\" & $txt
            $item = $parent
        EndIf
    Until $parent = 0
    Return $txt
EndFunc

Func _SearchFolder($folder,$parent,$level=0)
    If $level >= 1 Then Return
    $folders = _FileListToArray($folder,"*",2)
    _FolderFunc($folders,$folder,$parent,$level)
EndFunc

Func _FolderFunc($folders,$folder,$parent,$level)
    For $i = 1 To UBound($folders)-1
        $parentitem = _GUICtrlTreeView_AddChild($iTree,$parent,$folders[$i],0)
        _SearchFolder($folder & "\" & $folders[$i],$parentitem,$level+1)
    Next
EndFunc

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR
    $hWndTreeView = GUICtrlGetHandle($iTree)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $hWndTreeView
            Switch $iCode
                Case -451
                    $item = _GUICtrlTreeView_GetSelection($hWndTreeView)
                    $root = $item
                    If _GUICtrlTreeView_GetChildCount($hWndTreeView,$item) <= 0 Then
                        $txt = _GUICtrlTreeView_GetText($hWndTreeView,$item)
                        Do
                            $parent = _GUICtrlTreeView_GetParentHandle($hWndTreeView,$item)
                            If $parent <> 0 Then
                                $txt = _GUICtrlTreeView_GetText($hWndTreeView,$parent) & "\" & $txt
                                $item = $parent
                            EndIf
                        Until $parent = 0
                        _SearchFolder($txt,$root)
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _GUICtrlListView_CreateArray
; Description ...: Creates a 2-dimensional array from a listview.
; Syntax ........: _GUICtrlListView_CreateArray($hListView[, $sDelimeter = '|'])
; Parameters ....: $hListView           - Control ID/Handle to the control
;                  $sDelimeter          - [optional] One or more characters to use as delimiters (case sensitive). Default is '|'.
; Return values .: Success - The array returned is two-dimensional and is made up of the following:
;                                $aArray[0][0] = Number of rows
;                                $aArray[0][1] = Number of columns
;                                $aArray[0][2] = Delimited string of the column name(s) e.g. Column 1|Column 2|Column 3|Column nth

;                                $aArray[1][0] = 1st row, 1st column
;                                $aArray[1][1] = 1st row, 2nd column
;                                $aArray[1][2] = 1st row, 3rd column
;                                $aArray[n][0] = nth row, 1st column
;                                $aArray[n][1] = nth row, 2nd column
;                                $aArray[n][2] = nth row, 3rd column
; Author ........: guinness
; Remarks .......: GUICtrlListView.au3 should be included.
; Example .......: Yes
; ===============================================================================================================================
Func _GUICtrlListView_CreateArray($hListView, $sDelimeter = '|')
    Local $iColumnCount = _GUICtrlListView_GetColumnCount($hListView), $iDim = 0, $iItemCount = _GUICtrlListView_GetItemCount($hListView)
    If $iColumnCount < 3 Then
        $iDim = 3 - $iColumnCount
    EndIf
    If $sDelimeter = Default Then
        $sDelimeter = '|'
    EndIf

    Local $aColumns = 0, $aReturn[$iItemCount + 1][$iColumnCount + $iDim] = [[$iItemCount, $iColumnCount, '']]
    For $i = 0 To $iColumnCount - 1
        $aColumns = _GUICtrlListView_GetColumn($hListView, $i)
        $aReturn[0][2] &= $aColumns[5] & $sDelimeter
    Next
    $aReturn[0][2] = StringTrimRight($aReturn[0][2], StringLen($sDelimeter))

    For $i = 0 To $iItemCount - 1
        For $j = 0 To $iColumnCount - 1
            $aReturn[$i + 1][$j] = _GUICtrlListView_GetItemText($hListView, $i, $j)
        Next
    Next
    Return SetError(Number($aReturn[0][0] = 0), 0, $aReturn)
EndFunc   ;==>_GUICtrlListView_CreateArray




; #FUNCTION# ====================================================================================================================
; Name...........: _CSVSplit
; Description ...: Converts a string in CSV format to a two dimensional array (see comments)
; Syntax.........: CSVSplit ( $aArray [, $sDelim ] )
; Parameters ....: $aArray  - The array to convert
;                  $sDelim  - Optional - Delimiter set to comma by default (see 2nd comment)
; Return values .: Success  - Returns a two dimensional array or a one dimensional array (see 1st comment)
;                  Failure  - Sets @error to:
;                 |@error = 1 - First parameter is not a valid string
;                 |@error = 2 - Second parameter is not a valid string
;                 |@error = 3 - Could not find suitable delimiter replacements
; Author ........: czardas
; Comments ......; Returns a one dimensional array if the input string does not contain the delimiter string
;                ; Some CSV formats use semicolon as a delimiter instead of a comma
;                ; Set the second parameter to @TAB To convert to TSV
; ===============================================================================================================================
Func _CSVSplit($string, $sDelim = ",") ; Parses csv string input and returns a one or two dimensional array
    If Not IsString($string) Or $string = "" Then Return SetError(1, 0, 0) ; Invalid string
    If Not IsString($sDelim) Or $sDelim = "" Then Return SetError(2, 0, 0) ; Invalid string

    $string = StringRegExpReplace($string, "[\r\n]+\z", "") ; [Line Added] Remove training breaks
    Local $iOverride = 63743, $asDelim[3] ; $asDelim => replacements for comma, new line and double quote
    For $i = 0 To 2
        $asDelim[$i] = __GetSubstitute($string, $iOverride) ; Choose a suitable substitution character
        If @error Then Return SetError(3, 0, 0) ; String contains too many unsuitable characters
    Next
    $iOverride = 0

    Local $aArray = StringRegExp($string, '\A[^"]+|("+[^"]+)|"+\z', 3) ; Split string using double quotes delim - largest match
    $string = ""

    Local $iBound = UBound($aArray)
    For $i = 0 To $iBound -1
        $iOverride += StringInStr($aArray[$i], '"', 0, -1) ; Increment by the number of adjacent double quotes per element
        If Mod ($iOverride +2, 2) = 0 Then ; Acts as an on/off switch
            $aArray[$i] = StringReplace($aArray[$i], $sDelim, $asDelim[0]) ; Replace comma delimeters
            $aArray[$i] = StringRegExpReplace($aArray[$i], "(\r\n)|[\r\n]", $asDelim[1]) ; Replace new line delimeters
        EndIf
        $aArray[$i] = StringReplace($aArray[$i], '""', $asDelim[2]) ; Replace double quote pairs
        $aArray[$i] = StringReplace($aArray[$i], '"', '') ; Delete enclosing double quotes - not paired
        $aArray[$i] = StringReplace($aArray[$i], $asDelim[2], '"') ; Reintroduce double quote pairs as single characters
        $string &= $aArray[$i] ; Rebuild the string, which includes two different delimiters
    Next
    $iOverride = 0

    $aArray = StringSplit($string, $asDelim[1], 2) ; Split to get rows
    $iBound = UBound($aArray)
    Local $aCSV[$iBound][2], $aTemp
    For $i = 0 To $iBound -1
        $aTemp = StringSplit($aArray[$i], $asDelim[0]) ; Split to get row items
        If Not @error Then
            If $aTemp[0] > $iOverride Then
                $iOverride = $aTemp[0]
                ReDim $aCSV[$iBound][$iOverride] ; Add columns to accomodate more items
            EndIf
        EndIf
        For $j = 1 To $aTemp[0]
            If StringLen($aTemp[$j]) Then
                If Not StringRegExp($aTemp[$j], '[^"]') Then ; Field only contains double quotes
                    $aTemp[$j] = StringTrimLeft($aTemp[$j], 1) ; Delete enclosing double quote single char
                EndIf
                $aCSV[$i][$j -1] = $aTemp[$j] ; Populate each row
            EndIf
        Next
    Next

    If $iOverride > 1 Then
        Return $aCSV ; Multiple Columns
    Else
        For $i = 0 To $iBound -1
            If StringLen($aArray[$i]) And (Not StringRegExp($aArray[$i], '[^"]')) Then ; Only contains double quotes
                $aArray[$i] = StringTrimLeft($aArray[$i], 1) ; Delete enclosing double quote single char
            EndIf
        Next
        Return $aArray ; Single column
    EndIf

EndFunc ;==> _CSVSplit

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __GetSubstitute
; Description ...: Searches for a character to be used for substitution, ie one not contained within the input string
; Syntax.........: __GetSubstitute($string, ByRef $iCountdown)
; Parameters ....: $string   - The string of characters to avoid
;                  $iCountdown - The first code point to begin checking
; Return values .: Success   - Returns a suitable substitution character not found within the first parameter
;                  Failure   - Sets @error to 1 => No substitution character available
; Author ........: czardas
; Comments ......; This function is connected to the function _CSVSplit and was not intended for general use
;                  $iCountdown is returned ByRef to avoid selecting the same character on subsequent calls to this function
;                  Initially $iCountown should be passed with a value = 63743
; ===============================================================================================================================

Func __GetSubstitute($string, ByRef $iCountdown)
    If $iCountdown < 57344 Then Return SetError(1, 0, "") ; Out of options
    Local $sTestChar
    For $i = $iCountdown To 57344 Step -1
        $sTestChar = ChrW($i)
        $iCountdown -= 1
        If Not StringInStr($string, $sTestChar) Then
            Return $sTestChar
        EndIf
    Next
    Return SetError(1, 0, "") ; Out of options
EndFunc ;==> __GetSubstitute

 

Share this post


Link to post
Share on other sites

Out of curiosity, what if any parameters are you utilizing/depending on in the XML definition that you cannot set at task creation?
I've also been a fan of using robocopy for folder sync.

 

Share this post


Link to post
Share on other sites

I actually did it crude way. I created a task that I wanted in task scheduler and then exported the XML. Then I opened the XML with Notepad++ and did a regex replace on \r\n and replaced it with  '&@CRLF&' which gave me the string to build the xml file the way I wanted. I subbed out the filename and passed it through from the previous function. Basically I built this because some of my colleagues don't know how task scheduler works nor do they understand what a bat file is (most of them don't even know the difference between their desktop and their C drive). This was a super crude way of me pushing something very simple on to their machines that they can control. (I actually had to walk over to show someone how to open the control panel when the screenshots of how to open control panel from the start menu wasn't clear enough to them)

I avoided robocopy because I've had problems with admin priv on robocopy, but xcopy seems to work fine for me. I'm planning on blowing this out a little more to allow the list view to be right clicked so you can set the backup path for each folder otherwise they will default to the backup directory.

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