ControlClick with Power BI Desktop Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
SlCantre939
Posts: 11
Joined: 08 Feb 2023, 11:37

ControlClick with Power BI Desktop  Topic is solved

Post by SlCantre939 » 19 Feb 2023, 13:58

I'm trying to push the Refresh button on Power BI desktop.
Was successful in pushing it using the "AutomateMyTask" function by Joe Glines.
However, wanted to use controls so it would not be dependent on screen sizes and usable on different computers.
So I tried to switch to using ClickControl. After four days of working on it, still unsuccessful.
Sure I'm missing something simple.

Here is the button I'm trying to push. Attachment 1.
Power BI Button Bar.jpg
Power BI Desktop Button Bar
Power BI Button Bar.jpg (26.69 KiB) Viewed 899 times
Here is a snap of UIAViewer with the Win Title, Class NN, Name, Auotmationid, Attachment 2
UIAViewer Refresh.jpg
UIAView of the Refresh Button with all characteristics
UIAViewer Refresh.jpg (77.85 KiB) Viewed 899 times
The right window is being selected and the maximize function works on it.
Tried a number of different types of the ControlClick command and get no response from any of them.
Appreciate any help.
Sammy

Code: Select all


if !WinExist("GLS Admin Extract Views - Power BI Desktop")
{
  Run, "C:\Users\Sammy\Desktop\GLS Admin Extract Views.pbix"
  Sleep 10000
}
WaitforPBIX:
if !WinExist("GLS Admin Extract Views - Power BI Desktop")
  {
    ; OutputDebug "wait for pbix load released"
    Sleep 10000
    Goto WaitforPBIX
  }
  Else
  {
    sleep 10000
  }
WinActivate
Sleep 1000
WinMaximize
Sleep 1000

SetControlDelay -1
ControlClick, Refresh,ahk_class WindowsForms10.Window.8.app.0.9fe31_r6_ad1,,,, NA

ControlClick, Refresh, ahk_class ms-Button root-327,,,, NA

ControlClick, Refresh, ahk_class "ms-Button root-327",,,, NA

ControlClick, Refresh, GLS Admin Extract Views - Power BI Desktop,,,, NA

ControlClick, Refresh, AutomationId refreshQueries,,,, NA


Power BI Button Bar.jpg
Power BI Desktop Button Bar
Power BI Button Bar.jpg (26.69 KiB) Viewed 899 times

SlCantre939
Posts: 11
Joined: 08 Feb 2023, 11:37

Re: ControlClick with Power BI Desktop

Post by SlCantre939 » 19 Feb 2023, 15:20

Wanted to include that I am running this as administrator.

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: ControlClick with Power BI Desktop

Post by teadrinker » 19 Feb 2023, 17:08

This should work:

Code: Select all

F11::
   SetTitleMatchMode, 2
   if !hWnd := WinExist("Power BI Desktop") {
      MsgBox Power BI Desktop window not found
      ExitApp
   }
   UIAutomation_ClickButtonByName(hWnd, "Refresh")
   Return

UIAutomation_ClickButtonByName(hWnd, name) {
   static UIA_ControlTypePropertyId := 30003
        , UIA_NamePropertyId        := 30005
        , UIA_ButtonControlTypeId   := 50000
        , UIA_InvokePatternId       := 10000
        , TreeScope_Descendants := 0x4
        , VT_I4 := 0x3, VT_BSTR := 0x8
        
   Automation2 := IUIAutomation2.Create()
   Element := Automation2.ElementFromHandle(hWnd)
   Condition1 := Automation2.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId, VT_I4)
   pStr := DllCall("Oleaut32\SysAllocString", "WStr", name, "Ptr")
   Condition2 := Automation2.CreatePropertyCondition(UIA_NamePropertyId, pStr, VT_BSTR)
   AndCondition := Automation2.CreateAndCondition(Condition1.ptr, Condition2.ptr)
   Element := Element.FindFirst(TreeScope_Descendants, AndCondition.ptr)
   DllCall("Oleaut32\SysFreeString", "Ptr", pStr)
   if !Element {
      MsgBox Button not found
      Return
   }
   Automation2.put_AutoSetFocus(false)
   pInvokePattern := Element.GetCurrentPattern(UIA_InvokePatternId)
   InvokePattern := new IUIAutomationInvokePattern(pInvokePattern)
   InvokePattern.Invoke()
}

class IUIAutomation2 extends _InterfaceBase
{
   Create() {
      static CLSID_CUIAutomation8 := "{E22AD333-B25F-460C-83D0-0581107395C9}"
           , IID_IUIAutomation2   := "{34723AFF-0C9D-49D0-9896-7AB52DF8CD8A}"
      pIUIAutomation2 := ComObjCreate(CLSID_CUIAutomation8, IID_IUIAutomation2)
      Return new IUIAutomation2(pIUIAutomation2)      
   }
   ElementFromHandle(hwnd) {
      hr := DllCall(this.VTable(6), "Ptr", this.ptr, "Ptr", hwnd, "PtrP", pIUIAutomationElement)
      this.IsError(A_ThisFunc, hr)
      Return new IUIAutomationElement(pIUIAutomationElement)
   }
   CreatePropertyCondition(propertyId, value, varType) {
      if (A_PtrSize = 4)
         hr := DllCall(this.VTable(23), "Ptr", this.ptr, "Int", propertyId, "Int64", varType, "Int64", value, "PtrP", pIUIAutomationCondition)
      else {
         VarSetCapacity(variant, 24, 0)
         NumPut(varType, variant)
         NumPut(value, variant, 8)
         hr := DllCall(this.VTable(23), "Ptr", this.ptr, "Int", propertyId, "Ptr", &variant, "PtrP", pIUIAutomationCondition)
      }
      this.IsError(A_ThisFunc, hr)
      Return new IUIAutomationCondition(pIUIAutomationCondition)
   }
   CreateAndCondition(condition1, condition2) {
      hr := DllCall(this.VTable(25), "Ptr", this.ptr, "Ptr", condition1, "Ptr", condition2, "PtrP", pIUIAutomationCondition)
      this.IsError(A_ThisFunc, hr)
      Return new IUIAutomationCondition(pIUIAutomationCondition)
   }
   put_AutoSetFocus(bool) {
      hr := DllCall(this.VTable(59), "Ptr", this.ptr, "UInt", bool)
      this.IsError(A_ThisFunc, hr)
      Return hr
   }
}

class IUIAutomationElement extends _InterfaceBase
{
   FindFirst(scope, condition) {
      hr := DllCall(this.VTable(5), "Ptr", this.ptr, "Int", scope, "Ptr", condition, "PtrP", pIUIAutomationElement)
      this.IsError(A_ThisFunc, hr)
      Return pIUIAutomationElement ? new IUIAutomationElement(pIUIAutomationElement) : 0
   }
   GetCurrentPattern(patternId) {
      hr := DllCall(this.VTable(16), "Ptr", this.ptr, "Int", patternId, "PtrP", pPatternObject)
      this.IsError(A_ThisFunc, hr)
      Return pPatternObject
   }
}

class IUIAutomationCondition extends _InterfaceBase
{
}

class IUIAutomationInvokePattern extends _InterfaceBase
{ ; UIA_InvokePatternId := 10000
   Invoke() {
      hr := DllCall(this.VTable(3), "Ptr", this.ptr)
      this.IsError(A_ThisFunc, hr)
      Return hr
   }
}

class _InterfaceBase
{
   __New(ptr) {
      this.ptr := ptr
   }
   __Delete() {
      ObjRelease(this.ptr)
   }
   VTable(idx) {
      Return NumGet(NumGet(this.ptr + 0) + A_PtrSize*idx)
   }
   IsError(method, result, exc := true) {
      if (result = 0)
         Return 0
      error := StrReplace(method, ".", "::") . " failed.`nResult: "
                              . ( result = "" ? "No result" : this.SysErrorToText(Format("{:#x}", result & 0xFFFFFFFF)) )
                              . "`nErrorLevel: " . ErrorLevel
      if !exc
         Return error
      throw error
   }
   SysErrorToText(ErrorNum := "")
   { 
      static flags := (FORMAT_MESSAGE_ALLOCATE_BUFFER := 0x100) | (FORMAT_MESSAGE_FROM_SYSTEM := 0x1000)
      (ErrorNum = "" && ErrorNum := A_LastError)
      DllCall("FormatMessage", "UInt", flags, "UInt", 0, "UInt", ErrorNum, "UInt", 0, "PtrP", pBuff, "UInt", 512, "Str", "")
      str := StrGet(pBuff), DllCall("LocalFree", "Ptr", pBuff)
      Return str? str : ErrorNum
   }
}
SlCantre939 wrote: Wanted to include that I am running this as administrator.
Launch the script as administrator too.
Edited.
Last edited by teadrinker on 19 Feb 2023, 18:31, edited 1 time in total.

SlCantre939
Posts: 11
Joined: 08 Feb 2023, 11:37

Re: ControlClick with Power BI Desktop

Post by SlCantre939 » 19 Feb 2023, 19:00

Thank you this worked very well.
If I'm reading your function correctly, you switched to using a DLLCall to push the button.
Is that what it takes with this type of screen?

Thank you again.
Your function will be included in all my code from now on with Power BI.

Thanks

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: ControlClick with Power BI Desktop

Post by teadrinker » 19 Feb 2023, 19:18

SlCantre939 wrote: Is that what it takes with this type of screen?
No, that is what it takes with this type of button. AHK can't directly work with UIA elements, and doesn't recognize such elements as separate controls, so I tried UIA Automation methods.

SlCantre939
Posts: 11
Joined: 08 Feb 2023, 11:37

Re: ControlClick with Power BI Desktop

Post by SlCantre939 » 21 Mar 2023, 10:07

Teadrinker,
Thanks for the help.
Is there a reason that this UIA Automation script will not work on other controls on the same page?
I confirmed the hWnd is set correctly for the page, but can't get it to work with other controls.
Thanks again.

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: ControlClick with Power BI Desktop

Post by teadrinker » 21 Mar 2023, 13:28

SlCantre939 wrote: Is there a reason that this UIA Automation script will not work on other controls on the same page?
It depends on the control. The current version works with button-like controls only.

SlCantre939
Posts: 11
Joined: 08 Feb 2023, 11:37

Re: ControlClick with Power BI Desktop

Post by SlCantre939 » 21 Mar 2023, 15:55

Teadrinker,
Thanks again for the update.
How do I determine which type of controls can not be pressed with AHK?
What are the type of screens / apps that it does not work with.
Thanks

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: ControlClick with Power BI Desktop

Post by teadrinker » 21 Mar 2023, 20:43

SlCantre939 wrote: How do I determine which type of controls can not be pressed with AHK?
AHK can interact with any control using IUIAutomation (of course, if an application supports IUIAutomation). But this interaction must be properly handled in the code. It will be different for each type of controls.

Descolada
Posts: 1099
Joined: 23 Dec 2021, 02:30

Re: ControlClick with Power BI Desktop

Post by Descolada » 22 Mar 2023, 08:16

@SlCantre939, you can also use the UIA_Interface.ahk library that UIAViewer uses. An example to click a button would be something like this:

Code: Select all

#include UIA_Interface.ahk ; Place the file in the same folder as the script
UIA := UIA_Interface()
if !bi := UIA.ElementFromHandle("Power BI Desktop) {
      MsgBox Power BI Desktop window not found
      ExitApp
}
bi.FindFirst("Name='Refresh' and Type=Button").Highlight().Click()
Then you can target any type of element, or use AutomationId instead of Name, or some other way of locating the element.

Post Reply

Return to “Ask for Help (v1)”