Skip to content

Instantly share code, notes, and snippets.

@flipeador
Last active March 27, 2025 02:53
Show Gist options
  • Save flipeador/29ac92f277f4c01e07659f17f8c0caa5 to your computer and use it in GitHub Desktop.
Save flipeador/29ac92f277f4c01e07659f17f8c0caa5 to your computer and use it in GitHub Desktop.
A basic GUI for backing up files and directories with ROBOCOPY in Windows.
;@Ahk2Exe-SetFileVersion 1.0.4.0
;@Ahk2Exe-SetProductName Robocopy backup tool
;@Ahk2Exe-SetCopyright https://gist.github.com/flipeador/29ac92f277f4c01e07659f17f8c0caa5
#Requires AutoHotkey v2
#SingleInstance Off
#NoTrayIcon
TITLE := 'Robocopy backup tool'
if WinExist(TITLE . ' ahk_class AutoHotkeyGUI')
WinActivate(), ExitApp()
try action := A_Args[1]
defdiropts := '/mir /r:0'
; Alternate Data Stream for storing script data.
fdata := FileOpen(A_ScriptFullPath . ':data', 'rw-rwd')
ui := Gui(, TITLE)
ui.BackColor := 0xEDEDD1
ui.MarginX := ui.MarginY := 0
ui.SetFont(, 'Consolas')
lv := ui.AddListView(
Format('w{} R{} -LV0x10', 1000, 30),
['Source', 'Destination', 'Files', 'Options']
)
ui.AddButton('x5 vSave', 'Save')
ui['Save'].OnEvent('Click', OnSave)
ui.AddButton('x+20 yp vRun', 'Run')
ui['Run'].OnEvent('Click', OnRun)
ui.AddButton('x+5 yp vInit', 'Init')
ui['Init'].OnEvent('Click', OnInit)
ui.AddButton('x+20 yp vSrc', 'Source')
ui['Src'].OnEvent('Click', OnSrc)
ui.AddButton('x+5 yp vDst', 'Destination')
ui['Dst'].OnEvent('Click', OnDest)
ui.AddButton('x+5 yp vFile', 'Files')
ui['File'].OnEvent('Click', OnFile)
ui.AddButton('x+5 yp vOpt', 'Options')
ui['Opt'].OnEvent('Click', OnOptions)
ui.AddButton('x+20 yp vDel', 'Delete')
ui['Del'].OnEvent('Click', OnDelete)
ui.AddButton('x+20 yp vHelp', 'Help')
ui['Help'].OnEvent('Click', OnHelp)
SetExplorerTheme(lv.Hwnd)
SetMicaEffect(ui.Hwnd)
ui.OnEvent('Close', (*) => ExitApp())
loop parse fdata.Read(), '`n'
lv.Add(, StrSplit(A_LoopField, '`r')*)
; AutoHotkey.exe robocopy.ahk /run
if IsSet(action) && action = '/run' {
Exec(, '/q /c', 'Hide')
ExitApp()
}
lv.GetPos(,, &w)
lv.ModifyCol(1, w / 3)
lv.ModifyCol(2, w / 3)
lv.ModifyCol(3, 'AutoHdr')
ui.Show()
OnSave(*) {
fdata.Length := 0
loop lv.GetCount()
fdata.Write(
Format(
'{}`r{}`r{}`r{}`n',
lv.GetText(A_Index, 1),
lv.GetText(A_Index, 2),
lv.GetText(A_Index, 3),
lv.GetText(A_Index, 4)
)
)
--fdata.Length
}
Exec(ln1:='', params:='', options:='', fn:='') {
path := A_Temp . '\~robocopy.bat'
f := FileOpen(path, 'w')
f.Write(ln1 . '`r`n')
loop lv.GetCount() {
src := lv.GetText(A_Index, 1)
dst := lv.GetText(A_Index, 2)
files := lv.GetText(A_Index, 3)
opts := lv.GetText(A_Index, 4)
if !fn || fn(f, src, dst, files, opts)
f.Write(
Format(
'robocopy.exe "{}" "{}" {} {}`r`n',
fn ? dst : src,
fn ? src : dst,
files ? files : '',
files ? opts : opts || defdiropts
)
)
}
Run(Format('cmd.exe {} call "{}"', params, path),, options)
}
OnRun(*) {
Exec('pause', '/q /k')
}
OnInit(*) {
fn(f, src, dst, files, opts) {
return !DirExist(src)
}
Exec('pause', '/q /k', '', fn)
}
OnSrc(*) {
ui.Opt('+OwnDialogs')
if index := lv.GetNext()
src := lv.GetText(index, 1)
root := IsSet(src) ? src : ''
root := StrReplace(root, '%AppData%', A_AppData)
if !path := FileSelect('D2', root)
return
path := StrReplace(path, A_AppData, '%AppData%')
if !index
return lv.Add(, path)
lv.Modify(index,, path)
}
OnDest(*) {
ui.Opt('+OwnDialogs')
if index := lv.GetNext()
dest := lv.GetText(index, 2)
root := IsSet(dest) ? dest : ''
if !path := FileSelect('D2', root)
return
if !index
return lv.Add(,, path)
lv.Modify(index,,, path)
}
OnFile(*) {
ui.Opt('+OwnDialogs')
if index := lv.GetNext()
src := lv.GetText(index, 1)
root := IsSet(src) ? src : ''
list := FileSelect('M', root)
if !list.Length
return
files := ''
for path in list
SplitPath(path, &filename)
, files .= '"' . filename . '" '
if index
lv.Modify(index, 'Vis Select Focus',,, files)
else index := lv.Add('Vis Select Focus',,, files)
if !root
SplitPath(list[1],, &dir), lv.Modify(index,, dir)
}
OnOptions(*) {
ui.Opt('+OwnDialogs')
if index := lv.GetNext() {
file := lv.GetText(index, 3)
opts := lv.GetText(index, 4)
opts := file ? opts : opts || defdiropts
opts := InputBox(, 'Options',, opts)
if opts.Result = 'OK'
lv.Modify(index,,,,, opts.Value)
}
}
OnDelete(*) {
try lv.Delete(lv.GetNext())
}
OnHelp(*) {
Run('cmd.exe /q /k robocopy.exe /?')
}
SetExplorerTheme(hWnd) {
DllCall('uxtheme\SetWindowTheme', 'Ptr', hWnd, 'Str', 'Explorer', 'Ptr', 0)
}
SetMicaEffect(hWnd) {
if (VerCompare(A_OSVersion, '10.0.22621') >= 0)
DllCall('dwmapi\DwmSetWindowAttribute', 'Ptr', hWnd, 'Int', 20, 'IntP', 1, 'Int', 4)
, DllCall('dwmapi\DwmSetWindowAttribute', 'Ptr', hWnd, 'Int', 38, 'IntP', 3, 'Int', 4)
}
@flipeador
Copy link
Author

flipeador commented Jan 9, 2025

Run the script with AutoHotkey v2.

Quiet mode: AutoHotkey.exe robocopy.ahk /run.

The script must be stored on a file system that supports ADS: NTFS ReFS.


Press the Run button to mirror Source in Destination.

Press the Init button to mirror Destination in Source, if Source does not exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment