Jump to content

Task Schedule folder sync

Recommended Posts

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


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>

If FileExists($sNetbackup) = 0 Then DirCreate($sNetbackup)
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)
;Get old folders
For $p=0 to UBound($aImportFolders)-1

$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)
$root = _GUICtrlTreeView_AddChild($iTree,"","C:",0)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
        Case $cRemoveSelected
        Case $cRemoveAll
        Case $cAddSelected
        Case $cButton
            For $z=1 to UBound($aFolders)-1
                FileWriteLine($sScriptDir&"AutoBackup.bat",'xcopy "'&$aFolders[$z][0]&'\*" "H:\Backup\'&$sfoldername&'" /S /I')
            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>')
            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)
                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'.")
                MsgBox(0,"Process Complete","The saved task has been modified.")

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

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

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)

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)
                            $parent = _GUICtrlTreeView_GetParentHandle($hWndTreeView,$item)
                            If $parent <> 0 Then
                                $txt = _GUICtrlTreeView_GetText($hWndTreeView,$parent) & "\" & $txt
                                $item = $parent
                        Until $parent = 0

; #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
    If $sDelimeter = Default Then
        $sDelimeter = '|'

    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
    $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)
    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
    $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
        $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
    $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
        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
                $aCSV[$i][$j -1] = $aTemp[$j] ; Populate each row

    If $iOverride > 1 Then
        Return $aCSV ; Multiple Columns
        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
        Return $aArray ; Single column

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

  • Create New...