VeeDub Posted March 1, 2020 Share Posted March 1, 2020 Hello, I'm trying to write a script to allow pause / resume of Dropbox syncing; so that Dropbox syncing can be scheduled. So the idea is that the script will emulate what a user would normally do by clicking on Dropbox on the System Tray and then the script will toggle the sync operation If Dropbox currently syncing, will pause If syncing currently paused, will resume I'm writing this script for Windows 10 I am able to open Dropbox from the System Tray, but I can't figure out how to enumerate the controls. I would have been happy with the "simple" approach of using the Autoit Window Info tool to manually identify the values for the control. But unfortunately as soon as I click on the Finder Tool the Dropbox window closes. I then tried enumerating the controls with my script. I have experimented with a couple of scripts that others have shared, which while they don't error; don't return any values for the Controls. So at the moment I'm stuck and I'm after some ideas Is there a way to use a Windows Viewer tool with an app like Dropbox that doesn't remain visible? (I had a quick look but could not find a solution) If I need to enumerate the controls myself, here is my current script. I'm using _EnumChildWindows from here expandcollapse popup#include <Array.au3> #include <GuiToolBar.au3> #include <_EnumChildWindows.au3> Local $hSysTray_Handle Local $hWnd="",$hControl=0,$sTitle=0,$sClass=0,$aEnumList Local $hNumber1Button=-1,$hNumber4Button=-1,$hPlusButton=-1,$hEqualButton=-1 $sSearchtext='Dropbox' $iButton=Get_SysTray_IconText($sSearchtext) _GUICtrlToolbar_ClickButton($hSysTray_Handle, $iButton, "left", False, 1) ; MsgBox (64,'Searched Button','Button: '&$iButton&@CRLF&'Instance: '&@extended) If WinActivate($hSysTray_Handle, "") Then ; MsgBox($MB_SYSTEMMODAL, "", "Dropbox Window activated") ; Check Dropbox status - Either: ; 'Up to date' (i.e. syncing) ; 'Syncing paused' (paused) $hWnd = $hSysTray_Handle ; Important to wait for the window to fully 'create' itself before getting child windows! ; Note that other processes that become activated somewhere between WinWait and this will cause WinWaitActive() to wait for manual activation ;WinWaitActive($hWnd) ; bad idea in busy environment Sleep(3000) WinActivate($hWnd) ; this seems to be a better alternative, the window seems fully created after this is called in my tests ; Parameters to function ;$hControl=HWnd(0x########) ;$sTitle="^(\d|\+|=)$" ; PCRE - gets controls with numbers, + or = sign only (problem: no instance #'s!) ;$sClass="Button" $aEnumList=_EnumChildWindows($hWnd,$hControl,$sTitle,$sClass) ;,2) for RegExp Title If @Error Then Exit ; Find specific items [Certain versions of Calc won't return any text] For $i=1 to $aEnumList[0][0] Switch $aEnumList[$i][4] Case "1" $hNumber1Button=$aEnumList[$i][0] ConsoleWrite("'1' Advanced Mode Name (in current state): [CLASS:Button; INSTANCE:"&$aEnumList[$i][3]&"]"&@CRLF) Case "4" $hNumber4Button=$aEnumList[$i][0] ConsoleWrite("'4' Advanced Mode Name (in current state): [CLASS:Button; INSTANCE:"&$aEnumList[$i][3]&"]"&@CRLF) Case "+" $hPlusButton=$aEnumList[$i][0] ConsoleWrite("'+' Advanced Mode Name (in current state): [CLASS:Button; INSTANCE:"&$aEnumList[$i][3]&"]"&@CRLF) Case "=" $hEqualButton=$aEnumList[$i][0] ConsoleWrite("'=' Advanced Mode Name (in current state): [CLASS:Button; INSTANCE:"&$aEnumList[$i][3]&"]"&@CRLF) EndSwitch Next ; Add Headers $aEnumList[0][0]="Handle" $aEnumList[0][1]="Classname" $aEnumList[0][2]="Control ID" $aEnumList[0][3]="Iteration" $aEnumList[0][4]="Title/Text" ; Bring the window forward WinActivate($hWnd) ; Perform a simple calculation to show interaction If $hNumber1Button<>-1 Then ControlClick($hWnd,"",$hNumber1Button,"primary",3) ControlClick($hWnd,"",$hNumber4Button) Sleep(1000) ControlClick($hWnd,"",$hPlusButton) Sleep(1000) ControlClick($hWnd,"",$hNumber4Button,"primary",2) ; double click, but that's fine Sleep(1000) ControlClick($hWnd,"",$hEqualButton) EndIf ; And Display ALL Enumerated Windows _ArrayDisplay($aEnumList,"Enumerated controls for App") Else MsgBox($MB_SYSTEMMODAL + $MB_ICONERROR, "Error", "Dropbox Window not activated") EndIf Func Get_SysTray_IconText($sSearch) For $i = 1 To 99 ; Find systray handles $hSysTray_Handle = ControlGetHandle('[Class:Shell_TrayWnd]', '', '[Class:ToolbarWindow32;Instance:' & $i & ']') ; ConsoleWrite ("Handle: " & $hSysTray_Handle & @CRLF) If @error Then ;MsgBox(16, "Error", "System tray not found") ExitLoop EndIf ; Get systray item count Local $iSysTray_ButCount = _GUICtrlToolbar_ButtonCount($hSysTray_Handle) ConsoleWrite("iSysTray_ButCount: " & $iSysTray_ButCount & @CRLF ) If $iSysTray_ButCount = 0 Then ;MsgBox(16, "Error", "No items found in system tray") ContinueLoop EndIf Local $aSysTray_ButtonText[$iSysTray_ButCount] ; Look for wanted tooltip For $iSysTray_ButtonNumber = 0 To $iSysTray_ButCount - 1 ConsoleWrite("iSysTray_ButtonNumber: " & $iSysTray_ButtonNumber & " Button_Text: " & _GUICtrlToolbar_GetButtonText($hSysTray_Handle, $iSysTray_ButtonNumber) & @CRLF ) If $sSearch= StringLeft(_GUICtrlToolbar_GetButtonText($hSysTray_Handle, $iSysTray_ButtonNumber),StringLen($sSearch)) Then ConsoleWrite("Button Text: " & $sSearch & " Handle: " & $hSysTray_Handle & " Button Number: " & $iSysTray_ButtonNumber & @CRLF) Return SetError(0, $i, $iSysTray_ButtonNumber) EndIf Next Next Return SetError(1, -1, -1) EndFunc ;==>Get_SysTray_IconText Thanks, VW Link to comment Share on other sites More sharing options...
VeeDub Posted March 1, 2020 Author Share Posted March 1, 2020 I'm going to investigate UIASpy Link to comment Share on other sites More sharing options...
VeeDub Posted March 1, 2020 Author Share Posted March 1, 2020 I think UI Automation is the way to go however I have a question about how to specify the Dropbox window condition If you look at the UIASpy output you can see that Dropbox is identified as "Dropbox 91.4.548Up to date" I need to be able to specify the window condition using "Dropbox" only. Not sure how to do this. Link to comment Share on other sites More sharing options...
VeeDub Posted March 1, 2020 Author Share Posted March 1, 2020 Have encountered an error #include "Includes\CUIAutomation2.au3" Dropbox_Pause() Func Dropbox_Pause() ; Create UI Automation object Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation ) If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "$oUIAutomation ERR" & @CRLF ) ConsoleWrite( "$oUIAutomation OK" & @CRLF ) ; Get Desktop element Local $pDesktop, $oDesktop $oUIAutomation.GetRootElement( $pDesktop ) $oDesktop = ObjCreateInterface( $pDesktop, $sIID_IUIAutomationElement, $dtagIUIAutomationElement ) If Not IsObj( $oDesktop ) Then Return ConsoleWrite( "$oDesktop ERR" & @CRLF ) ConsoleWrite( "$oDesktop OK" & @CRLF ) ; --- Dropbox window --- ConsoleWrite( "--- Dropbox window ---" & @CRLF ) Local $pCondition $oUIAutomation.CreatePropertyCondition(StringLeft($UIA_NamePropertyId,7), "Dropbox", $pCondition ) If Not $pCondition Then Return ConsoleWrite( "$pCondition ERR" & @CRLF ) ConsoleWrite( "$pCondition OK" & @CRLF ) Local $pDropbox, $oDropbox $oDesktop.FindFirst( $TreeScope_Descendants, $pCondition, $pDropbox ) $oDropbox = ObjCreateInterface( $pDropbox, $sIID_IUIAutomationElement, $dtagIUIAutomationElement ) If Not IsObj( $oDropbox ) Then Return ConsoleWrite( "$oDropbox ERR" & @CRLF ) ConsoleWrite( "$oDropbox OK" & @CRLF ) EndFunc Script output >"C:\Program Files (x86)\AutoIt3\SciTE\..\AutoIt3.exe" "C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.au3" /run /prod /ErrorStdOut /in "C:\ZEN\UI Automation\Code\Dropbox.Pause.au3" /UserParams +>19:11:23 Starting AutoIt3Wrapper (19.1127.1402.0} from:SciTE.exe (4.2.0.0) Keyboard:00000409 OS:WIN_2016/ CPU:X64 OS:X64 Environment(Language:0409) CodePage:0 utf8.auto.check:4 +> SciTEDir => C:\Program Files (x86)\AutoIt3\SciTE UserDir => C:\Users\ZEN\AppData\Local\AutoIt v3\SciTE\AutoIt3Wrapper SCITE_USERHOME => C:\Users\ZEN\AppData\Local\AutoIt v3\SciTE >Running AU3Check (3.3.14.5) from:C:\Program Files (x86)\AutoIt3 input:C:\ZEN\UI Automation\Code\Dropbox.Pause.au3 +>19:11:23 AU3Check ended.rc:0 >Running:(3.3.14.5):C:\Program Files (x86)\AutoIt3\autoit3.exe "C:\ZEN\UI Automation\Code\Dropbox.Pause.au3" +>Setting Hotkeys...--> Press Ctrl+Alt+Break to Restart or Ctrl+BREAK to Stop. $oUIAutomation OK $oDesktop OK --- Dropbox window --- $pCondition OK $oDropbox ERR +>19:11:26 AutoIt3.exe ended.rc:0 +>19:11:26 AutoIt3Wrapper Finished. >Exit code: 0 Time: 3.147 Link to comment Share on other sites More sharing options...
LarsJ Posted March 1, 2020 Share Posted March 1, 2020 This will not work: $oUIAutomation.CreatePropertyCondition(StringLeft($UIA_NamePropertyId,7), "Dropbox", $pCondition ) When you're on Windows 10, you might as well take advantage of the new capabilities: expandcollapse popup#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 ;#AutoIt3Wrapper_UseX64=n ; If target application is running as 32 bit code ;#AutoIt3Wrapper_UseX64=y ; If target application is running as 64 bit code #include "UIA_Constants.au3" ; Can be copied from UIASpy Includes folder ;#include "UIA_Functions.au3" ; Can be copied from UIASpy Includes folder ;#include "UIA_SafeArray.au3" ; Can be copied from UIASpy Includes folder ;#include "UIA_Variant.au3" ; Can be copied from UIASpy Includes folder Opt( "MustDeclareVars", 1 ) Example() Func Example() ; Create UI Automation object Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation8, $sIID_IUIAutomation6, $dtag_IUIAutomation6 ) If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "$oUIAutomation ERR" & @CRLF ) ConsoleWrite( "$oUIAutomation OK" & @CRLF ) ; Get Desktop element Local $pDesktop, $oDesktop $oUIAutomation.GetRootElement( $pDesktop ) $oDesktop = ObjCreateInterface( $pDesktop, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oDesktop ) Then Return ConsoleWrite( "$oDesktop ERR" & @CRLF ) ConsoleWrite( "$oDesktop OK" & @CRLF ) ; --- Find window/control --- ConsoleWrite( "--- Find window/control ---" & @CRLF ) Local $pCondition0, $pCondition1, $pAndCondition1 $oUIAutomation.CreatePropertyCondition( $UIA_ClassNamePropertyId, "Shell_TrayWnd", $pCondition0 ) $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_PaneControlTypeId, $pCondition1 ) $oUIAutomation.CreateAndCondition( $pCondition0, $pCondition1, $pAndCondition1 ) If Not $pAndCondition1 Then Return ConsoleWrite( "$pAndCondition1 ERR" & @CRLF ) ConsoleWrite( "$pAndCondition1 OK" & @CRLF ) Local $pPane1, $oPane1 $oDesktop.FindFirst( $TreeScope_Children, $pAndCondition1, $pPane1 ) $oPane1 = ObjCreateInterface( $pPane1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oPane1 ) Then Return ConsoleWrite( "$oPane1 ERR" & @CRLF ) ConsoleWrite( "$oPane1 OK" & @CRLF ) ; --- Find window/control --- ConsoleWrite( "--- Find window/control ---" & @CRLF ) Local $pCondition2, $pCondition3, $pAndCondition3 $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $pCondition2 ) $oUIAutomation.CreatePropertyConditionEx( $UIA_NamePropertyId, "Dropbox", $PropertyConditionFlags_MatchSubstring, $pCondition3 ) ; Windows 10-1809 $oUIAutomation.CreateAndCondition( $pCondition2, $pCondition3, $pAndCondition3 ) If Not $pAndCondition3 Then Return ConsoleWrite( "$pAndCondition3 ERR" & @CRLF ) ConsoleWrite( "$pAndCondition3 OK" & @CRLF ) Local $pButton1, $oButton1 $oPane1.FindFirst( $TreeScope_Descendants, $pAndCondition3, $pButton1 ) $oButton1 = ObjCreateInterface( $pButton1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oButton1 ) Then Return ConsoleWrite( "$oButton1 ERR" & @CRLF ) ConsoleWrite( "$oButton1 OK" & @CRLF ) EndFunc Remember that if you are using UIASpy to generate Windows 10 code, then choose Options | Windows | Mode | Windows 10 Last. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
VeeDub Posted March 3, 2020 Author Share Posted March 3, 2020 @Larsj Thanks for pointing me in the right direction. I have to say that I did wonder about the use of StringLeft, but when that line executed without error I figured it may have been OK. Unfortunately I'm still struggling with a lot of the detail at this point. My understanding of the development process is that we need to identify and find the window, which is a "drill down" process starting from the desktop and then having identified the Window, identify the control and perform some action. When I describe it like that, it seems fairly straight-forward; but as I get into the detail I soon get lost. So looking at your code: We start with the Desktop, I understand that Then we goto the Shell_TrayWnd, I understand that Then we goto Dropbox, I don't understand that When I look at UIASpy on my system I see the following: Pane: Desktop1 Pane: Taskbar (Shell_TrayWnd) Pane: TrayNotifyWnd Pane: SysPager ToolBar Button:Dropbox 91.4.548Syncing paused Now of course if your example works and I don't have to "drill down" as I describe above that's great. But I need to understand how you worked that out. So in your example you have gone directly from the 2nd level in the "chain", to the destination Window/object (Dropbox). There are also quite a few examples that have been posted on how to use UIA. Generally I often refer to an example involving Notepad as it is a pretty "simple" app and so examples are usually straight-forward. But I'm not sure that the Notepad example is the most applicable for me in this case? My next question is probably going to be how to write the code to click on the Dropbox button; but hopefully you will be able to refer me to a more relevant example that I can use as a reference for this script. I have to say that my impression is that this UI Automation development environment that you have created - or provided access to via AutoIt - is extremely powerful. And if I can master the basics then there is a lot that can be done. But the learning curve seems a little steep. The irony is that in pseudocode what I want to do is really quite simple Left-click on Dropbox icon on the ToolBar Dropbox Window appears Left-click on Dropbox button (Access and manage your Dropbox settings) A menu will appear Which will have a number of items, one of which will either be Pause syncing Resume syncing Left-click on either menu item which is present It is quite simple and at this stage I have no idea how to do it. Cheers VW Xandy 1 Link to comment Share on other sites More sharing options...
LarsJ Posted March 3, 2020 Share Posted March 3, 2020 8 hours ago, VeeDub said: Now of course if your example works and I don't have to "drill down" as I describe above that's great. But I need to understand how you worked that out. I've read the Microsoft documentation and know that depending on the TreeScope parameter, the FindFirst() method can search through an entire subtree in a single search. But when you open the Dropbox window, you must identify the window as a UIA element before you can search it. And when you open the menu you must also identify the menu window as a UIA element before you can search it. The notepad example seems to contain all the techniques you need. It should also be relatively easy to implement your pseudocode as real code. You should be able to click the Dropbox icon with this code: ; --- Invoke Pattern (action) Object --- ConsoleWrite( "--- Invoke Pattern (action) Object ---" & @CRLF ) Local $pInvokePattern1, $oInvokePattern1 $oButton1.GetCurrentPattern( $UIA_InvokePatternId, $pInvokePattern1 ) $oInvokePattern1 = ObjCreateInterface( $pInvokePattern1, $sIID_IUIAutomationInvokePattern, $dtag_IUIAutomationInvokePattern ) If Not IsObj( $oInvokePattern1 ) Then Return ConsoleWrite( "$oInvokePattern1 ERR" & @CRLF ) ConsoleWrite( "$oInvokePattern1 OK" & @CRLF ) ; --- Invoke Pattern (action) Methods --- ConsoleWrite( "--- Invoke Pattern (action) Methods ---" & @CRLF ) $oInvokePattern1.Invoke() ConsoleWrite( "$oInvokePattern1.Invoke()" & @CRLF ) Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
VeeDub Posted March 5, 2020 Author Share Posted March 5, 2020 @Larsj Your code worked. I've been able to finish the code and it looks to be working fine, but I will monitor for a day or two before sharing the code just in case there needs to be some fine-tuning. I noticed initially that occasionally a Dropbox 'script paused' dialog would appear and stop the script from running. I've added the #NoTrayIcon option to the script which appears to have resolved that behaviour. Thanks again for all the work you have done in making interacting with the GUI more accessible and in giving me some direction with the specifics of this script 👍 VW Link to comment Share on other sites More sharing options...
VeeDub Posted March 5, 2020 Author Share Posted March 5, 2020 (edited) @Larsj When I run my program via Task Scheduler it doesn't work. Yet when I run it by double-clicking in Explorer it works fine. Are you aware of any issues introduced by using the Task Scheduler? Here is the Dropbox_Resume function in case I've goofed somewhere. expandcollapse popup#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Outfile=Dropbox.Resume.exe #AutoIt3Wrapper_Compression=3 #AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_UseX64=n ; If target application is running as 32 bit code ;#AutoIt3Wrapper_UseX64=y ; If target application is running as 64 bit code #NoTrayIcon #include "Includes\UIA_Constants.au3" ; Can be copied from UIASpy Includes folder ;#include "Includes\UIA_Functions.au3" ; Can be copied from UIASpy Includes folder ;#include "Includes\UIA_SafeArray.au3" ; Can be copied from UIASpy Includes folder ;#include "IncludesUIA_Variant.au3" ; Can be copied from UIASpy Includes folder Opt( "MustDeclareVars", 1 ) Dropbox_Resume() Func Dropbox_Resume() ; Create UI Automation object Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation8, $sIID_IUIAutomation6, $dtag_IUIAutomation6 ) If Not IsObj( $oUIAutomation ) Then Return ConsoleWrite( "$oUIAutomation ERR" & @CRLF ) ConsoleWrite( "$oUIAutomation OK" & @CRLF ) ; Get Desktop element Local $pDesktop, $oDesktop $oUIAutomation.GetRootElement( $pDesktop ) $oDesktop = ObjCreateInterface( $pDesktop, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oDesktop ) Then Return ConsoleWrite( "$oDesktop ERR" & @CRLF ) ConsoleWrite( "$oDesktop OK" & @CRLF ) ; --- Find window/control --- ConsoleWrite( "--- Find window/control ---" & @CRLF ) Local $pCondition0, $pCondition1, $pAndCondition1 $oUIAutomation.CreatePropertyCondition( $UIA_ClassNamePropertyId, "Shell_TrayWnd", $pCondition0 ) $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_PaneControlTypeId, $pCondition1 ) $oUIAutomation.CreateAndCondition( $pCondition0, $pCondition1, $pAndCondition1 ) If Not $pAndCondition1 Then Return ConsoleWrite( "$pAndCondition1 ERR" & @CRLF ) ConsoleWrite( "$pAndCondition1 OK" & @CRLF ) Local $pPane1, $oPane1 $oDesktop.FindFirst( $TreeScope_Children, $pAndCondition1, $pPane1 ) $oPane1 = ObjCreateInterface( $pPane1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oPane1 ) Then Return ConsoleWrite( "$oPane1 ERR" & @CRLF ) ConsoleWrite( "$oPane1 OK" & @CRLF ) ; --- Find window/control --- ConsoleWrite( "--- Find window/control ---" & @CRLF ) Local $pCondition2, $pCondition3, $pAndCondition3 $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $pCondition2 ) $oUIAutomation.CreatePropertyConditionEx( $UIA_NamePropertyId, "Dropbox", $PropertyConditionFlags_MatchSubstring, $pCondition3 ) ; Windows 10-1809 $oUIAutomation.CreateAndCondition( $pCondition2, $pCondition3, $pAndCondition3 ) If Not $pAndCondition3 Then Return ConsoleWrite( "$pAndCondition3 ERR" & @CRLF ) ConsoleWrite( "$pAndCondition3 OK" & @CRLF ) Local $pButton1, $oButton1 $oPane1.FindFirst( $TreeScope_Descendants, $pAndCondition3, $pButton1 ) $oButton1 = ObjCreateInterface( $pButton1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oButton1 ) Then Return ConsoleWrite( "$oButton1 ERR" & @CRLF ) ConsoleWrite( "$oButton1 OK" & @CRLF ) ; --- Invoke Pattern (action) Object --- ConsoleWrite( "--- Invoke Pattern (action) Object ---" & @CRLF ) Local $pInvokePattern1, $oInvokePattern1 $oButton1.GetCurrentPattern( $UIA_InvokePatternId, $pInvokePattern1 ) $oInvokePattern1 = ObjCreateInterface( $pInvokePattern1, $sIID_IUIAutomationInvokePattern, $dtag_IUIAutomationInvokePattern ) If Not IsObj( $oInvokePattern1 ) Then Return ConsoleWrite( "$oInvokePattern1 ERR" & @CRLF ) ConsoleWrite( "$oInvokePattern1 OK" & @CRLF ) ; --- Invoke Pattern (action) Methods --- ConsoleWrite( "--- Invoke Pattern (action) Methods ---" & @CRLF ) $oInvokePattern1.Invoke() ConsoleWrite( "$oInvokePattern1.Invoke()" & @CRLF ) Sleep(100) ; --- Click on 'Resume syncing' if menu option available --- Local $pCondition4 $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_MenuItemControlTypeId, $pCondition4) If Not $pCondition4 Then Return ConsoleWrite( "$pCondition4 ERR" & @CRLF) ConsoleWrite("$pCondition4 OK" & @CRLF) Local $pCondition5 $oUIAutomation.CreatePropertyCondition( $UIA_NamePropertyId, "Resume syncing", $pCondition5 ) ; 'Resume syncing' <<<<<<<<<<<<<<<<<<<< If Not $pCondition5 Then Return ConsoleWrite( "$pCondition5 ERR" & @CRLF ) ConsoleWrite( "$pCondition5 OK" & @CRLF ) ; And condition $oUIAutomation.CreateAndCondition( $pCondition4, $pCondition5, $pCondition0 ) If Not $pCondition0 Then Return ConsoleWrite( "$pCondition ERR" & @CRLF ) ConsoleWrite( "$pCondition0 OK" & @CRLF ) Local $pResumeSync, $oResumeSync $oDesktop.FindFirst( $TreeScope_Descendants, $pCondition0, $pResumeSync ) $oResumeSync = ObjCreateInterface( $pResumeSync, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oResumeSync ) Then Return ConsoleWrite( "$oResumeSync ERR" & @CRLF ) ConsoleWrite( "$oResumeSync OK" & @CRLF ) Local $pInvoke, $oInvoke $oResumeSync.GetCurrentPattern( $UIA_InvokePatternId, $pInvoke ) $oInvoke = ObjCreateInterface( $pInvoke, $sIID_IUIAutomationInvokePattern, $dtag_IUIAutomationInvokePattern ) If Not IsObj( $oInvoke ) Then Return ConsoleWrite( "$oInvoke ERR" & @CRLF ) ConsoleWrite( "$oInvoke OK" & @CRLF ) $oInvoke.Invoke() Sleep( 100 ) EndFunc Edited March 5, 2020 by VeeDub Link to comment Share on other sites More sharing options...
VeeDub Posted March 5, 2020 Author Share Posted March 5, 2020 Looks like the answer may be here I'm still working through this at the moment Link to comment Share on other sites More sharing options...
argumentum Posted March 5, 2020 Share Posted March 5, 2020 11 minutes ago, VeeDub said: Are you aware of any issues introduced by using the Task Scheduler? is it with the user on a screen ?, else there is nothing to click-click. Does the task run with the rights of the running app. ... oh, you found it cool Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted March 5, 2020 Share Posted March 5, 2020 ..would you care to try Syncthing ? I like it better. seadoggie01 1 Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
VeeDub Posted March 5, 2020 Author Share Posted March 5, 2020 The joys of trying to do stuff in Windows This post by @leunamme explains the issue very well. By default tasks launched via the Task Scheduler are launched in a non-interactive session, so they can't interact with the GUI. There is a work-around suggested, which is to launch psexec using the -i switch for 'interactive' and then call your program that way. If I try doing that from the cmd line (admittedly in a session that is already interactive) it works. But when I try the same thing from the Task Scheduler, same behaviour as before. There is another method suggested, I'll probably try that next. In the meantime I'll have a break and think about it, in case I'm making a mistake somewhere here. Link to comment Share on other sites More sharing options...
VeeDub Posted March 5, 2020 Author Share Posted March 5, 2020 OK well the good news is that I have figured this out. Using the other method proposed by @leunamme works If you create the Scheduled Tasks via the command-line schtasks and specify the /IT switch it does work. Good luck with using schtasks though if you want to schedule for certain days of the week etc. - the input checking is a PITA Link to comment Share on other sites More sharing options...
VeeDub Posted March 6, 2020 Author Share Posted March 6, 2020 (edited) Well this has ended up being more involved than I had anticipated As stated above, if you want to schedule your script to be executed via the Task Scheduler, then the /IT switch needs to be used to create the scheduled task (to make the task run interactively) and this switch can only be enabled for a scheduled task via the command-line: schtasks utility. However compared to the GUI Task Scheduler; schtasks is quite clunky. But once the Scheduled Task has been created it appears in the GUI Task Scheduler and can be modified. So I would suggest creating the schedule task initially specifying the bare essentials with schtasks, and then modifying the config using the GUI after. This would be: /RU = user /RP = password /SC = once (schedule - then reconfigure in the GUI as desired) /ST = 08:00 (start time - then change as needed) /TN = Task name /TR = Task executable /IT = Interactive @larsj The other issue that I've encountered is that my script doesn't seem to work when the console is locked. Having a look at that post by @leunamme he says Quote Now the question may arise, why should I use ControlClick/ControlSend rather than Send/MouseClick? Well even if one is not developing gamebots (which may need clicking into minimized windows), a lot of GUI automating scripts may fail when the workstation is locked as Send and MouseClick get disabled by the system. So to get a script running and automating a GUI when the workstation is locked or another app gets focus before the script is through etc one needs to use ControlClick/ControlSend. I'm guessing that $oInvoke.Invoke via ObjCreateInterface is effectively using Send/MouseClick rather than ControlClick/ControlSend. Do you have any experience / ideas about how to use ControlClick/ControlSend? Edited March 6, 2020 by VeeDub Link to comment Share on other sites More sharing options...
VeeDub Posted March 7, 2020 Author Share Posted March 7, 2020 Unfortunately the suggestion I made above regarding using Schtasks to created the Scheduled Task initially and then using the GUI Task Scheduler to modify the properties of the scheduled task doesn't work. It appears that when you make changes to the Scheduled Task using the GUI Scheduler, the /IT (interactive) switch is lost. So the task has to be created with Schtasks and any changes to the schedule have to be made via Schtasks By logging the program output to a file; I've been able to compare behaviour when running the program via the Scheduler with the console unlocked (works) versus console locked (task hangs - and doesn't work) expandcollapse popup2020-03-07 Sat 11:28 $oUIAutomation OK 2020-03-07 Sat 11:28 $oDesktop OK 2020-03-07 Sat 11:28 --- Find window/control --- 2020-03-07 Sat 11:28 $pAndCondition1 OK 2020-03-07 Sat 11:28 $oPane1 OK 2020-03-07 Sat 11:28 --- Find window/control --- 2020-03-07 Sat 11:28 $pAndCondition3 OK 2020-03-07 Sat 11:28 $oButton1 OK 2020-03-07 Sat 11:28 --- Invoke Pattern (action) Object --- 2020-03-07 Sat 11:28 $oInvokePattern1 OK 2020-03-07 Sat 11:28 --- Invoke Pattern (action) Methods --- 2020-03-07 Sat 11:28 $oInvokePattern1.Invoke() 2020-03-07 Sat 11:28 $pCondition4 OK 2020-03-07 Sat 11:28 $pCondition5 OK 2020-03-07 Sat 11:28 $pCondition0 OK 2020-03-07 Sat 11:28 $oPauseSync OK 2020-03-07 Sat 11:28 $oInvoke OK ----------------------------------------------------- 2020-03-07 Sat 11:33 $oUIAutomation OK 2020-03-07 Sat 11:33 $oDesktop OK 2020-03-07 Sat 11:33 --- Find window/control --- 2020-03-07 Sat 11:33 $pAndCondition1 OK 2020-03-07 Sat 11:33 $oPane1 OK 2020-03-07 Sat 11:33 --- Find window/control --- 2020-03-07 Sat 11:33 $pAndCondition3 OK 2020-03-07 Sat 11:33 $oButton1 OK 2020-03-07 Sat 11:33 --- Invoke Pattern (action) Object --- 2020-03-07 Sat 11:33 $oInvokePattern1 OK 2020-03-07 Sat 11:33 --- Invoke Pattern (action) Methods --- Here is the code expandcollapse popup#NoTrayIcon #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Compression=3 #AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ;#AutoIt3Wrapper_UseX64=y ; If target application is running as 64 bit code #include "Includes\UIA_Constants.au3" ; Can be copied from UIASpy Includes folder ;#include "Includes\UIA_Functions.au3" ; Can be copied from UIASpy Includes folder ;#include "Includes\UIA_SafeArray.au3" ; Can be copied from UIASpy Includes folder ;#include "IncludesUIA_Variant.au3" ; Can be copied from UIASpy Includes folder Opt( "MustDeclareVars", 1 ) #include <Date.au3> Global $Event_Log = @ScriptDir & "\Pause_EventLog.txt" Global $sShortDayName = _DateDayOfWeek( @WDAY, 1 ) Dropbox_Pause() Func Dropbox_Pause() ; Create UI Automation object Local $oUIAutomation = ObjCreateInterface( $sCLSID_CUIAutomation8, $sIID_IUIAutomation6, $dtag_IUIAutomation6 ) If Not IsObj( $oUIAutomation ) Then Return UpdateEventLog( "$oUIAutomation ERR" & @CRLF ) UpdateEventLog( "$oUIAutomation OK" & @CRLF ) ; Get Desktop element Local $pDesktop, $oDesktop $oUIAutomation.GetRootElement( $pDesktop ) $oDesktop = ObjCreateInterface( $pDesktop, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oDesktop ) Then Return UpdateEventLog( "$oDesktop ERR" & @CRLF ) UpdateEventLog( "$oDesktop OK" & @CRLF ) ; --- Find window/control --- UpdateEventLog( "--- Find window/control ---" & @CRLF ) Local $pCondition0, $pCondition1, $pAndCondition1 $oUIAutomation.CreatePropertyCondition( $UIA_ClassNamePropertyId, "Shell_TrayWnd", $pCondition0 ) $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_PaneControlTypeId, $pCondition1 ) $oUIAutomation.CreateAndCondition( $pCondition0, $pCondition1, $pAndCondition1 ) If Not $pAndCondition1 Then Return UpdateEventLog( "$pAndCondition1 ERR" & @CRLF ) UpdateEventLog( "$pAndCondition1 OK" & @CRLF ) Local $pPane1, $oPane1 $oDesktop.FindFirst( $TreeScope_Children, $pAndCondition1, $pPane1 ) $oPane1 = ObjCreateInterface( $pPane1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oPane1 ) Then Return UpdateEventLog( "$oPane1 ERR" & @CRLF ) UpdateEventLog( "$oPane1 OK" & @CRLF ) ; --- Find window/control --- UpdateEventLog( "--- Find window/control ---" & @CRLF ) Local $pCondition2, $pCondition3, $pAndCondition3 $oUIAutomation.CreatePropertyCondition( $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $pCondition2 ) $oUIAutomation.CreatePropertyConditionEx( $UIA_NamePropertyId, "Dropbox", $PropertyConditionFlags_MatchSubstring, $pCondition3 ) ; Windows 10-1809 $oUIAutomation.CreateAndCondition( $pCondition2, $pCondition3, $pAndCondition3 ) If Not $pAndCondition3 Then Return UpdateEventLog( "$pAndCondition3 ERR" & @CRLF ) UpdateEventLog( "$pAndCondition3 OK" & @CRLF ) Local $pButton1, $oButton1 $oPane1.FindFirst( $TreeScope_Descendants, $pAndCondition3, $pButton1 ) $oButton1 = ObjCreateInterface( $pButton1, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oButton1 ) Then Return UpdateEventLog( "$oButton1 ERR" & @CRLF ) UpdateEventLog( "$oButton1 OK" & @CRLF ) ; --- Invoke Pattern (action) Object --- UpdateEventLog( "--- Invoke Pattern (action) Object ---" & @CRLF ) Local $pInvokePattern1, $oInvokePattern1 $oButton1.GetCurrentPattern( $UIA_InvokePatternId, $pInvokePattern1 ) $oInvokePattern1 = ObjCreateInterface( $pInvokePattern1, $sIID_IUIAutomationInvokePattern, $dtag_IUIAutomationInvokePattern ) If Not IsObj( $oInvokePattern1 ) Then Return UpdateEventLog( "$oInvokePattern1 ERR" & @CRLF ) UpdateEventLog( "$oInvokePattern1 OK" & @CRLF ) ; --- Invoke Pattern (action) Methods --- UpdateEventLog( "--- Invoke Pattern (action) Methods ---" & @CRLF ) $oInvokePattern1.Invoke() UpdateEventLog( "$oInvokePattern1.Invoke()" & @CRLF ) Sleep(100) ; --- Click on 'Pause syncing' if menu option available --- Local $pCondition4 $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_MenuItemControlTypeId, $pCondition4) If Not $pCondition4 Then Return UpdateEventLog( "$pCondition4 ERR" & @CRLF) UpdateEventLog("$pCondition4 OK" & @CRLF) Local $pCondition5 $oUIAutomation.CreatePropertyCondition( $UIA_NamePropertyId, "Pause syncing", $pCondition5 ) ; 'Pause syncing' <<<<<<<<<<<<<<<<<<<< If Not $pCondition5 Then Return UpdateEventLog( "$pCondition5 ERR" & @CRLF ) UpdateEventLog( "$pCondition5 OK" & @CRLF ) ; And condition $oUIAutomation.CreateAndCondition( $pCondition4, $pCondition5, $pCondition0 ) If Not $pCondition0 Then Return UpdateEventLog( "$pCondition ERR" & @CRLF ) UpdateEventLog( "$pCondition0 OK" & @CRLF ) Local $pPauseSync, $oPauseSync $oDesktop.FindFirst( $TreeScope_Descendants, $pCondition0, $pPauseSync ) $oPauseSync = ObjCreateInterface( $pPauseSync, $sIID_IUIAutomationElement9, $dtag_IUIAutomationElement9 ) If Not IsObj( $oPauseSync ) Then Return UpdateEventLog( "$oPauseSync ERR" & @CRLF ) UpdateEventLog( "$oPauseSync OK" & @CRLF ) Local $pInvoke, $oInvoke $oPauseSync.GetCurrentPattern( $UIA_InvokePatternId, $pInvoke ) $oInvoke = ObjCreateInterface( $pInvoke, $sIID_IUIAutomationInvokePattern, $dtag_IUIAutomationInvokePattern ) If Not IsObj( $oInvoke ) Then Return UpdateEventLog( "$oInvoke ERR" & @CRLF ) UpdateEventLog( "$oInvoke OK" & @CRLF ) $oInvoke.Invoke() Sleep( 100 ) EndFunc Func UpdateEventLog($Event) Local $log Local $Space = " " ; Update Daily Event Log $log = FileOpen($Event_Log,1) ; Check if file opened for reading OK If $log = -1 Then ; MsgBox(0, "Error", "Unable to open Event_Log") Exit EndIf ; Retrieve the abbreviated name $sShortDayName = _DateDayOfWeek( @WDAY, 1 ) FileWriteLine($log, @YEAR & "-" & @MON & "-" & @MDAY & $Space & $sShortDayName & $Space & @HOUR & ":" & @MIN & $Space & $Event & @CRLF) FileClose($log) EndFunc ;==>UpdateEventLog The code that doesn't execute successfully when the console is locked $oInvokePattern1.Invoke() So the click on the SystemTray Dropbox icon fails and causes the script to hang. Link to comment Share on other sites More sharing options...
dmob Posted March 8, 2020 Share Posted March 8, 2020 (edited) Try this script by ptrex ... Oops, just realised he quoted you in that script {facepalm} Edited March 8, 2020 by dmob {facepalm} Link to comment Share on other sites More sharing options...
VeeDub Posted March 10, 2020 Author Share Posted March 10, 2020 I'm trying to write a new version that will work when the console is locked So far I have the following code, using _GUICtrlToolBar_ClickButton that successfully clicks on Dropbox on the System Tray when the console is unlocked, but doesn't work when the console is locked. How do I know? Because I list the active Windows after executing ClickButton and when the console is locked Dropbox is not listed expandcollapse popup#NoTrayIcon #include <GuiToolBar.au3> #include <MsgBoxConstants.au3> ; Sleep(10000) Local $hSysTray_Handle Local $Index Local $SysTray_ButtonText Local $Command_ID For $Index = 1 To 99 $hSysTray_Handle = ControlGetHandle('[Class:Shell_TrayWnd]', '', '[Class:ToolbarWindow32;Instance:' & $Index & ']') Local $iSysTray_ButCount = _GUICtrlToolbar_ButtonCount($hSysTray_Handle) ; ConsoleWrite("SysTray Button Count: " & $iSysTray_ButCount & @CRLF) For $Command_ID = 0 To 99 $SysTray_ButtonText = _GUICtrlToolbar_GetButtonText($hSysTray_Handle,$Command_ID) ; ConsoleWrite("Index: " & $Index & " Button Text: " & $SysTray_ButtonText & @CRLF) If StringInStr($SysTray_ButtonText,"Dropbox") Then ExitLoop 2 EndIf Next ; Command_ID Next ; Index ConsoleWrite("Index: " & $Index & " Command ID: " & $Command_ID & " Button Text: " & $SysTray_ButtonText & @CRLF) ; Click on Dropbox icon on SystemTray _GUICtrlToolbar_ClickButton($hSysTray_Handle,$Command_ID,"Right",True) Sleep(100) ; Enumerate active Windows Local $aList = WinList() ; Loop through the array displaying only visable windows with a title. For $i = 1 To $aList[0][0] If $aList[$i][0] <> "" And BitAND(WinGetState($aList[$i][1]), 2) Then ConsoleWrite("Title: " & $aList[$i][0] & @TAB & "Handle: " & $aList[$i][1] & @CRLF) EndIf Next I then tried replacing _GUICtrlToolBar_ClickButton with ControlClick However the arguments for ControlClick don't seem suited to a ToolBar as there is no ability to send the $Command_ID $Status = ControlClick('[Class:Shell_TrayWnd]',"", '[Class:ToolbarWindow32;Instance:' & $Index & ']') ConsoleWrite("Status: " & $Status & @CRLF) The arguments for ControlClick are essentially the same as those for ControlGetHandle. Ironically when I use the example above it clicks on the icon adjacent to Dropbox on the System Tray. I have thought about experimenting with the X, Y coordinates with ControlClick to perhaps move the location of the virtual pointer but I'm not clear at all on how to determine the potential range of values to try. Moreover when I try the values reported by Windows Info - nothing is clicked. Essentially I'm stuck at the moment and I'm after some ideas to consider Link to comment Share on other sites More sharing options...
seadoggie01 Posted March 10, 2020 Share Posted March 10, 2020 (edited) I just looked into Syncthing... and while it doesn't do the same thing as Dropbox (Dropbox hosts your files on a server while Syncthing --get this-- syncs files between two computers), there is a very easy POST request to pause syncing between your clients. And they are VERY security conscious and open source, which makes me want to use them even more. Dropbox has very specifically said a few times that they really don't care about a command line option for this on Windows. A possible work-around is to use pssuspend as mentioned here. Edited March 10, 2020 by seadoggie01 All my code provided is Public Domain... but it may not work. Use it, change it, break it, whatever you want. Spoiler My Humble Contributions:Personal Function Documentation - A personal HelpFile for your functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types Link to comment Share on other sites More sharing options...
VeeDub Posted March 11, 2020 Author Share Posted March 11, 2020 @seadoggie01 Thanks for taking the time to respond. Somebody else recommended Syncthing, so I will consider that if I can't work out a way to control Dropbox sync that I am comfortable with. However at this stage I'm keen to try and work out how to interact with apps / system tray when the console is locked; because that is something that could be useful not only with this task, but also with others. I find it really surprising that UIA doesn't work when the console is locked; that to me is a major shortcoming. Unfortunately those work-arounds for Dropbox that you have found don't work. I have tried them before resorting to this. Pausing (or killing (more brutal)) the Dropbox processes used to work quite reliably. But in more recent times while that will still work to 'pause' syncing. Often time the resume doesn't work until the computer is restarted. I'm sure there is a reason for this, but the specifics of it are beyond me. That is why I have ventured down this path, because if you pause / resume Dropbox as a user would - then pausing / resuming works fine. And in fact my UIA script works fine so long as the desktop remains unlocked. But long-term that isn't best practice and I'm not going to keep doing that. At this stage in the absence of any other suggestions I think I'll play around with _GUIToolbar_Clickbutton to identify which part of the function "breaks" when the console is locked and then hope to try and figure out how to resolve this. Cheers, VW Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now