Best way to wrap rsync progress in a gui? - windows

I use rsync to synchronize files to Windows clients in a server agnostic way. What methods are available to send the progress of rsync to the parent process for display in a gui progress bar?
I imagine two or three choices exist. (1) Watch STDOUT (2) Watch rsync.exe log file, similar to unix tail (3) Watch rsync console output in memory.
Which one is best/preferred?

For this type of tasks, I use my own AutoIt script (freeware, Windows only). The script redirects the standard output into a graphical window, displaying it with the ability to scroll back, etc (very useful in long processes like XCOPYs / PKZIPs to check if any error did happen).
I use AutoIt because it's free, very easy to use, and can compile quickly into an .EXE. I think it's an excellent alternative to a complete programming language for this type of tasks. The downside is that it's for Windows only.
$sCmd = "DIR E:\*.AU3 /S" ; Test command
$nAutoTimeout = 10 ; Time in seconds to close window after finish
$nDeskPct = 60 ; % of desktop size (if percent)
; $nHeight = 480 ; height/width of the main window (if fixed)
; $nWidth = 480
$sTitRun = "Executing process. Wait...." ;
$sTitDone = "Process done" ;
$sSound = #WindowsDir & "\Media\Ding.wav" ; End Sound
$sButRun = "Cancel" ; Caption of "Exec" button
$sButDone = "Close" ; Caption of "Close" button
#include <GUIConstants.au3>
#include <Constants.au3>
#Include <GuiList.au3>
Opt("GUIOnEventMode", 1)
if $nDeskPct > 0 Then
$nHeight = #DesktopHeight * ($nDeskPct / 100)
$nWidth = #DesktopWidth * ($nDeskPct / 100)
EndIf
If $CmdLine[0] > 0 Then
$sCmd = ""
For $nCmd = 1 To $CmdLine[0]
$sCmd = $sCmd & " " & $CmdLine[$nCmd]
Next
; MsgBox (1,"",$sCmd)
EndIf
; AutoItSetOption("GUIDataSeparatorChar", Chr(13)+Chr(10))
$nForm = GUICreate($sTitRun, $nWidth, $nHeight)
GUISetOnEvent($GUI_EVENT_CLOSE, "CloseForm")
$nList = GUICtrlCreateList ("", 10, 10, $nWidth - 20, $nHeight - 50, $WS_BORDER + $WS_VSCROLL)
GUICtrlSetFont (-1, 9, 0, 0, "Courier New")
$nClose = GUICtrlCreateButton ($sButRun, $nWidth - 100, $nHeight - 40, 80, 30)
GUICtrlSetOnEvent (-1, "CloseForm")
GUISetState(#SW_SHOW) ;, $nForm)
$nPID = Run(#ComSpec & " /C " & $sCmd, ".", #SW_HIDE, $STDOUT_CHILD)
; $nPID = Run(#ComSpec & " /C _RunErrl.bat " & $sCmd, ".", #SW_HIDE, $STDOUT_CHILD) ; # Con ésto devuelve el errorlevel en _ERRL.TMP
While 1
$sLine = StdoutRead($nPID)
If #error Then ExitLoop
If StringLen ($sLine) > 0 then
$sLine = StringReplace ($sLine, Chr(13), "|")
$sLine = StringReplace ($sLine, Chr(10), "")
if StringLeft($sLine, 1)="|" Then
$sLine = " " & $sLine
endif
GUICtrlSetData ($nList, $sLine)
_GUICtrlListSelectIndex ($nList, _GUICtrlListCount ($nList) - 1)
EndIf
Wend
$sLine = " ||"
GUICtrlSetData ($nList, $sLine)
_GUICtrlListSelectIndex ($nList, _GUICtrlListCount ($nList) - 1)
GUICtrlSetData ($nClose, $sButDone)
WinSetTitle ($sTitRun, "", $sTitDone)
If $sSound <> "" Then
SoundPlay ($sSound)
EndIf
$rInfo = DllStructCreate("uint;dword") ; # LASTINPUTINFO
DllStructSetData($rInfo, 1, DllStructGetSize($rInfo));
DllCall("user32.dll", "int", "GetLastInputInfo", "ptr", DllStructGetPtr($rInfo))
$nLastInput = DllStructGetData($rInfo, 2)
$nTime = TimerInit()
While 1
If $nAutoTimeout > 0 Then
DllCall("user32.dll", "int", "GetLastInputInfo", "ptr", DllStructGetPtr($rInfo))
If DllStructGetData($rInfo, 2) <> $nLastInput Then
; Tocó una tecla
$nAutoTimeout = 0
EndIf
EndIf
If $nAutoTimeout > 0 And TimerDiff ($nTime) > $nAutoTimeOut * 1000 Then
ExitLoop
EndIf
Sleep (100)
Wend
Func CloseForm()
Exit
EndFunc

.NET has a pretty straight forward way to read and watch STDOUT.
I guess this would be the cleanest way, since it is not dependent on any external files, just the path to rsync. I would not be too surprised if there is a wrapper library out there either. If not, write and open source it :)

I've built my own simple object for this, I get a lot of reuse out of it, I can wrap it with a cmdline, web page, webservice, write output to a file, etc---
The commented items contain some rsync examples--
what I'd like to do sometime is embed rsync (and cygwin) into a resource & make a single .net executable out of it--
Here you go:
Imports System.IO
Namespace cds
Public Class proc
Public _cmdString As String
Public _workingDir As String
Public _arg As String
Public Function basic() As String
Dim sOut As String = ""
Try
'Set start information.
'Dim startinfo As New ProcessStartInfo("C:\Program Files\cwRsync\bin\rsync", "-avzrbP 192.168.42.6::cdsERP /cygdrive/s/cdsERP_rsync/gwy")
'Dim startinfo As New ProcessStartInfo("C:\Program Files\cwRsync\bin\rsync", "-avzrbP 10.1.1.6::user /cygdrive/s/cdsERP_rsync/gws/user")
'Dim startinfo As New ProcessStartInfo("C:\windows\system32\cscript", "//NoLogo c:\windows\system32\prnmngr.vbs -l")
Dim si As New ProcessStartInfo(_cmdString, _arg)
si.UseShellExecute = False
si.CreateNoWindow = True
si.RedirectStandardOutput = True
si.RedirectStandardError = True
si.WorkingDirectory = _workingDir
' Make the process and set its start information.
Dim p As New Process()
p.StartInfo = si
' Start the process.
p.Start()
' Attach to stdout and stderr.
Dim stdout As StreamReader = p.StandardOutput()
Dim stderr As StreamReader = p.StandardError()
sOut = stdout.ReadToEnd() & ControlChars.NewLine & stderr.ReadToEnd()
'Dim writer As New StreamWriter("out.txt", FileMode.CreateNew)
'writer.Write(sOut)
'writer.Close()
stdout.Close()
stderr.Close()
p.Close()
Catch ex As Exception
sOut = ex.Message
End Try
Return sOut
End Function
End Class
End Namespace

Check out DeltaCopy. It is a Windows GUI for rsync.

Check NAsBackup Its open source software that give Windows user Rsync GUI using Watch STDOUT.

Related

Looping through WinList() result takes much time

I get a list of open windows and check if it contains a certain title. It is working but takes more than 10 seconds. Why does it take so long, what is wrong with my code?
Looks like WinList() doesn't list only visible windows.
$title = 0
$begintime = TimerInit()
MsgBox($MB_OK, "Timer", "Timer inicialized")
While $title = 0
$aList = WinList()
For $x = 1 To $aList[0][0]
;Check if a window with this title exists.
if $aList[$x][0] = "WindowTitle" Then
If $lastruntitle = "WindowTitle" Then
$title = 1
ExitLoop(2)
Else
SendMail4()
$lastruntitle = "WindowTitle"
$title = 1
ExitLoop(2)
EndIf
EndIf
Next
WEnd
Simple solution for your task is:
#include <Array.au3>
While 1
$aList = WinList()
_ArraySearch($aList, "WindowTitle", 0, 0, 0, 0, 1, 0)
If Not #error Then
MsgBox(0,"","Window found!")
Exit
EndIf
Sleep(100)
WEnd

Auto it how to check if window appears and close

How do I check if a specific windoww appears in Autoit.
At the moment I am running with Auto it Adobe After Effect,
so far so good.
The problem is that a warning message pops up if the user does not have quick time installed.
Now I want to check if that window appears and is active and then close it.
So far I have this but did not work:
Local $iPID = Run("C:\Program Files\Adobe\Adobe After Effects CC 2015\Support Files\AfterFX.exe", "", #SW_SHOWMAXIMIZED)
WinWait("[CLASS:AfterEffects]", "", 1000)
Sleep(200000)
; if qicktime warning eror appears
If WinExists ("DroverLord - Window Class", "") Then
Send ("{ENTER}")
EndIf
Does this help?
Opt("WinDetectHiddenText", 1) ;0=don't detect, 1=do detect
Opt("WinSearchChildren", 1) ;0=no, 1=search children also
Opt("WinTextMatchMode", 1) ;1=complete, 2=quick
Opt("WinTitleMatchMode", 2) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase
#include<Date.au3>
Local $iPID = Run("C:\Program Files\Adobe\Adobe After Effects CC 2015\Support Files\AfterFX.exe", "", #SW_SHOWMAXIMIZED)
If #error Then
ConsoleWrite('ERROR' & #CRLF)
Exit(0)
EndIf
Global $end = False
Do
; if qicktime warning eror appears
If WinExists("DroverLord - Window Class", "") Then
ConsoleWrite('!FOUND ' & _NowTime() & #CRLF)
Send("{ENTER}") ;close?
WinClose("DroverLord - Window Class", "") ; this
WinKill("DroverLord - Window Class", "") ; or this
$end = True
EndIf
Until $end
Here is an some example code of what you are trying to do:
Local $fDiff
Local $sAfterFXPath = "C:\Program Files\Adobe\Adobe After Effects CC 2015\Support Files\AfterFX.exe"
If FileExists($sAfterFXPath) Then
Local $iPID = Run($sAfterFXPath, "", #SW_SHOWMAXIMIZED)
;no need to call WinExists becuase you are waiting for it to exist and be active with WinActivate
Local $hTimer = TimerInit() ; Begin the timer and store the handle in a variable.
Do
$fDiff = TimerDiff($hTimer)
Until WinActive("Title you are looking for") Or $fDiff >= 30000 ;<<<will exit loop when the window is active or after 30 seconds
If WinActive("Title you are looking for") Then
;Closes the window now that it is active
WinClose("Title you are looking for")
Else
MsgBox(0, "", "The window was never active.")
EndIf
Else
MsgBox(0, "", "File path not found. Do something else...")
EndIf

Create new folder and highlight it for renaming

I was trying to create an AutoIt script to create a new folder and then highlight it for renaming like if we right click and create a new folder.
Here is my script. What I wanted to achieve is create the new folder, highlight it, then press F2.
#include <WinAPI.au3>
HotKeySet("#y", "NewFolder")
While 1
Sleep(200)
WEnd
Func NewFolder()
Local $hWnd = WinGetHandle("[ACTIVE]")
Local $class = _WinAPI_GetClassName($hWnd)
If StringCompare($class, "CabinetWClass") = 0 Then
Local $sSelected_Path = _GetWindowsExplorerPath($hWnd) & "\NewFolder" & $count
Local $iFileExists = FileExists($sSelected_Path)
If $iFileExists Then
$count += 1
Else
Local $success = DirCreate($sSelected_Path)
Sleep(500)
If $success = 1 Then
Local $Finditem = ControlListView($hWnd, "", "[CLASS:SysListView32; INSTANCE:1]", "Finditem", "NewFolder")
MsgBox(0, "", $Finditem)
Local $Select = ControlListView($hWnd, "", "[CLASS:SysListView32; INSTANCE:1]", "Select", $Finditem)
$count += 1
EndIf
EndIf
EndIf
EndFunc
Func _GetWindowsExplorerPath($hWnd)
Local $pv, $pidl, $return = "", $ret, $hMem, $pid, $folderPath = DllStructCreate("char[260]"), $className
Local $bPIDL = False
Local Const $CWM_GETPATH = 0x400 + 12;
; Check the classname of the window first
$className = DllCall("user32.dll", "int", "GetClassName", "hwnd", $hWnd, "str", "", "int", 4096)
If #error Then Return SetError(2, 0, "")
If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
; Retrieve the process ID for our process
$pid = DllCall("kernel32.dll", "int", "GetCurrentProcessId")
If #error Then Return SetError(2, 0, "")
; Send the CWM_GETPATH message to the window
$hMem = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $CWM_GETPATH, "wparam", $pid[0], "lparam", 0)
If #error Then Return SetError(2, 0, "")
If $hMem[0] = 0 Then Return SetError(1, 0, "")
; Lock the shared memory
$pv = DllCall("shell32.dll", "ptr", "SHLockShared", "uint", $hMem[0], "uint", $pid[0])
If #error Then Return SetError(2, 0, "")
If $pv[0] Then
$pidl = DllCall("shell32.dll", "ptr", "ILClone", "uint", $pv[0]) ; Clone the PIDL
If #error Then Return SetError(2, 0, "")
$bPIDL = True
DllCall("shell32.dll", "int", "SHUnlockShared", "uint", $pv) ; Unlock the shared memory
EndIf
DllCall("shell32.dll", "int", "SHFreeShared", "uint", $hMem, "uint", $pid) ; Free the shared memory
If $bPIDL Then
; Retrieve the path from the PIDL
$ret = DllCall("shell32.dll", "int", "SHGetPathFromIDList", "ptr", $pidl[0], "ptr", DllStructGetPtr($folderPath))
If (#error = 0) And ($ret[0] <> 0) Then $return = DllStructGetData($folderPath, 1) ; Retrieve the value
DllCall("shell32.dll", "none", "ILFree", "ptr", $pidl[0]) ; Free up the PIDL that we cloned
Return SetError(0, 0, $return) ; Success
EndIf
Return SetError(2, 0, "") ; Failed a WinAPI call
EndFunc
This works for me in Win7 - should work in xp as long as address bar is turned on. MUCH simpler than using the DLL calls. I left you a little bit of work to do :)
HotKeySet("#y","_NewFolder")
HotKeySet("#n","_EXIT")
While 1
Sleep(250)
WEnd
FUNC _NewFolder() ; make all vars local
$TEXT = WinGetText("[active]")
; handle error here
$SPLIT = StringSplit($TEXT,#CR & #LF & #CRLF) ;split on new lines (idk which one of the three it uses)
IF IsArray($SPLIT) Then
; trigger = true
FOR $I = 1 to $SPLIT[0]
IF StringInStr($SPLIT[$i],"Address: ") Then
; trigger = false
$STRING = StringReplace($SPLIT[$i],"Address: ","")
$INPUT = InputBox("New Folder","Name your new folder")
; add some enforcement to input box, i like do until loops
$PATH = $STRING & "\" & $INPUT
$CREATE = DirCreate($PATH)
; handle error here
Return SetError(0,0,$PATH)
EndIf
Next
; if trigger then error
Else
Return SetError(1,0,0) ;if split is not an array - could add some more here in case address is the only line returned
EndIf
EndFunc
Func _Exit()
Exit
EndFunc
"create a new folder and then highlight it for renaming like its supposed to"
As per Documentation - Function Reference - DirCreate() :
Creates a directory/folder.
Example:
Global Const $g_sPathFolder = #DesktopDir & '\'
Global Const $g_sPromptText = 'Enter folder name :'
Global Const $g_sPromptExmp = 'NewFolder'
Global $g_sNameFolder = InputBox(#ScriptName, $g_sPromptText, $g_sPromptExmp)
DirCreate($g_sPathFolder & $g_sNameFolder)
ShellExecute($g_sPathFolder & $g_sNameFolder)

Windows command-line disk cataloging - save to csv?

I'm working on a Windows batch script that creates a directory/file listing of a complete hard disk for archival/cataloging purposes, using only command line-tools (and open-source/free tools). For each of the entries in the listing I wanted to list the filename, directory where it resides in, the filesize, date a,nd time of the file, and the md5 sum. I have been able to create somewhat a working starting point, but I'm hitting a wall since I'm not sure if it is even possible using the command-line tools in Windows. The command "dir /s /a:-d /o:-d /t:c" gives me a nice overview, but I would like this overview displayed (or saved to) a comma-delimited format. So my questions are:
Can I create a csv file with all the fields I mentioned above, with the standard command-line tools (and a m5 freeware tool for the md5 sums)
Do you know of a better way, or is there a dead simple disk cataloging command-line tool I missed?
Thanks in advance for any tips!
You can use dir /s /a:-d /o:-d /t:c > slam.txt
Then the content of this slam.txt, can be processed by WScript in windows, making a CSV file ...
If you need a WScript ex, I can provide one ?
I know this not an CSV example - but it should be complex enough for pattern inspiration :)
and remember this fil is saved as .js
var what2lookfor = '<rect ';
var forReading = 1, forWriting = 2, forAppending = 8, jx = 0, ix = 0;
var triStateUseDefault = -2, triStateTrue = -1, triStateFalse = 0;
var thisRecord="", validFileTypes="js,xml,txt,php,xsl,css,htm,html" , akkum = "";
var fileArray = [];
var FSO = new ActiveXObject("Scripting.FileSystemObject");
var objFiles = FSO.GetFolder("F:\\xps1710\\jscript\\");
var objFileControl = new Enumerator(objFiles.files);
for (; !objFileControl.atEnd(); objFileControl.moveNext()) {
objFile = FSO.GetFile(objFileControl.item());
var ext = objFile.Name.split(".");
if (validFileTypes.indexOf(ext[1]) > 1) {
fileArray[ix] = "F:\\xps1710\\jscript\\" + objFile.Name;
ix++;
}
}
for (zx = 0 ; zx < ix ; zx++ ) {
var file2Traverse = FSO.OpenTextFile(fileArray[zx], forReading, triStateUseDefault );
while (!file2Traverse.AtEndOfStream) {
thisRecord = file2Traverse.ReadLine();
if (thisRecord.indexOf(what2lookfor) > 1 ) {
akkum = akkum + fileArray[zx] + '::' + thisRecord + '\n';
break;
}
}
}
WScript.Echo(akkum);

How to Winwait for two windows simultaneously in AutoIt?

I would like to know if its possible to WinWaitActive for WindowWithThisTitle and WindowWithThatTitle at the same time. I'm executing a command and there could be a window telling me that the connection failed or a user/pass dialog coming up.
Is there another way doing it as this?
WinWaitActive("Title1", "", 5)
If(WinExists("Title1")) Then
MsgBox(0, "", "Do something")
Else
If(WinExists("Title2")) Then
MsgBox(0, "", "Do something else")
EndIf
EndIf
Because I don't want to have the timeout which could be more than 15 seconds.
A simpler solution might be to use a REGEX title in your WinWaitActive as defined here
You would then have something like this:
$hWnd = WinWaitActive("[REGEXPTITLE:(WindowWithThisTitle|WindowWithThatTitle)]")
If WinGetTitle($hWnd) = "WindowWithThisTitle" then
DoSomething()
Else
DoSomethingElse()
EndIf
How about something like this.
$stillLooking = True
While $stillLooking
$activeWindowTitle = WinGetTitle(WinActive(""))
If $activeWindowTitle == "Title1" Then
MsgBox(0, "", "Do something")
$stillLooking = False
ElseIf $activeWindowTitle == "Title2" Then
MsgBox(0, "", "Do something else")
$stillLooking = False
EndIf
sleep(5)
WEnd
Because I don't want to have the
timeout which could be more than 15
seconds.
WinWaitActive() doesn't have a timeout unless you specify one. You gave it a five second timeout but you could leave that off and it would wait forever.
You can use this Functions for two windows ..
; #FUNCTION# ====================================================================================================================
; Name...........: _2WinWait
; Description ...: Wait For Tow Windows .
; Syntax.........: _2WinWait ($FirstTitle,$SecondTitle,[$FirstText = "" ,[$SecondText = ""]] )
; Parameters ....: $FirstTitle - Title Of First Wondow
; $SecondTitle - Title Of Second Wondow
; $FirstText - Text Of First Wondow
; $SecondText - Text Of Second Wondow
; Return values .: Success - None
; Failure - Returns a 0 => If Your Titles Is Wrong
; Author ........: Ashalshaikh : Ahmad Alshaikh
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; No
; ===============================================================================================================================
Func _2WinWait ($FirstTitle,$SecondTitle,$FirstText = "" ,$SecondText = "" )
If $FirstTitle = "" Or $SecondTitle = "" Then
Return 0
Else
Do
Until WinExists ($FirstTitle,$FirstText) Or WinExists ($SecondTitle,$SecondText)
EndIf
EndFunc
; #FUNCTION# ====================================================================================================================
; Name...........: _2WinWait_Any
; Description ...: Wait For Tow Windows And Return Any Window Id Exists .
; Syntax.........: _2WinWait_Any ($FirstTitle,$SecondTitle,[$FirstText = "" ,[$SecondText = ""]] )
; Parameters ....: $FirstTitle - Title Of First Wondow
; $SecondTitle - Title Of Second Wondow
; $FirstText - Text Of First Wondow
; $SecondText - Text Of Second Wondow
; Return values .: Success - Number Of Window ==> 1= First Window , 2= Second Window
; Failure - Returns a 0 => If Your Titles Is Wrong
; Author ........: Ashalshaikh : Ahmad Alshaikh
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; No
; ===============================================================================================================================
Func _2WinWait_Any ($FirstTitle,$SecondTitle,$FirstText = "" ,$SecondText = "" )
If $FirstTitle = "" Or $SecondTitle = "" Then
Return 0
Else
Do
Until WinExists ($FirstTitle,$FirstText) Or WinExists ($SecondTitle,$SecondText)
If WinExists ($FirstTitle,$FirstTexit) Then
Return 1
Else
Return 2
EndIf
EndIf
EndFunc
for more with examples
I'm fairly new to autoit and the programming world in general and I had this same dilemma. Luckily I figured out a straight fwd way to do it:
Do
$var1 = 0
If WinGetState("Document Reference","") Then
$var1 = 1
ElseIf WinGetState("Customer Search","") Then
$var1 = 1
EndIf
Until $var1 = 1
So it'll stay in the loop until it finds the window and sets $var1 to 1. There's probably easier ways (I'm sure developers are gasping at this) but this is straight fwd enough for me.
You can create an infinite while loop with if statements in there:
#include <MsgBoxConstants.au3>
Example()
Func Example()
While 1
; Test if the window exists and display the results.
If WinExists("Windows Security") Then
Local $hWnd = WinWaitActive("Windows Security", "", 2000)
ControlSetText($hWnd, "", "[CLASS:Edit; INSTANCE:1]", "hel233")
ControlClick("Windows Security","","[CLASS:Button; INSTANCE:2]")
Sleep(5000)
EndIf
; Test if the window exists and display the results.
If WinExists("Spread the Word") Then
'The line below will wait until the window is active, but we don't need that
'Local $hWnd = WinWaitActive("Spread the Word", "", 2000)
WinClose("Spread the Word")
Sleep(5000)
EndIf
wend
EndFunc

Resources