Jump to content

How to enumerate controls for Dropbox


VeeDub
 Share

Recommended Posts

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

  1. 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)
  2. If I need to enumerate the controls myself, here is my current script. I'm using _EnumChildWindows from here
#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

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.

UIASpy_Dropbox.png

Link to comment
Share on other sites

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

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:

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

Link to comment
Share on other sites

@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

 

 

Link to comment
Share on other sites

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 )

 

Link to comment
Share on other sites

@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

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

#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 by VeeDub
Link to comment
Share on other sites

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.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

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

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

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 by VeeDub
Link to comment
Share on other sites

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)

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

#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

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

#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

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 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 functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

@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

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
 Share

×
×
  • Create New...