Jump to content

Automate ComboBox using UI Automation


Go to solution Solved by Andreik,

Recommended Posts

I am playing with ComboBox control in Connect to Server window in Microsoft SQL Server Management Studio application for Windows 10.

I used UI Spy on that control and I can read all values INSIDE that ComboBox (Database Engine, Analysis Services, Reporting Servcies, Integration Services) but when I try to read it with AutoIt then I got no infomation about it, I can't read it. Any solution?

I am also wondering how to change the value on that ComboBox. Should I click on that button (with small arrow facing  down on the right) to show all options and then read new elements via UI Automation annd then click/invoke on specified option or maybe there is better and faster way to do it?

image.thumb.png.562dd35b12b25b46770ff476062cc936.png

#include "CUIAutomation2.au3"

MSSMS_ServerTypeCheckbox_Test()

Func MSSMS_ServerTypeCheckbox_Test()
    ;UI Automation object initiation
    Global $UIA_oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation)
    If Not IsObj($UIA_oUIAutomation) Or @error Then Exit MsgBox(0, 0, "Error during creating $UIA_oUIAutomation object.")

    Local $handle = WinGetHandle("Microsoft SQL Server Management Studio")
    If @error Then Exit MsgBox(0, 0, "window not found")

    Local $ae_MSSM_ConnectToServer = UIA_GetAeFromHandle($handle)
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_MSSM_ConnectToServer")

    Local $ae_Window_Connect = UIA_GetAeFirstFromCondition($ae_MSSM_ConnectToServer, "AutomationID", "ConnectionDialog")
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_Window_Connect")

    Local $ae_Pane1 = UIA_GetAeFirstFromCondition($ae_Window_Connect, "AutomationID", "simpleView")
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_Pane1")

    Local $ae_Pane2 = UIA_GetAeFirstFromCondition($ae_Pane1, "AutomationID", "loginHolder")
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_Pane2")

    Local $ae_Pane3 = UIA_GetAeFirstFromCondition($ae_Pane2, "AutomationID", "LoginControl")
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_Pane3")

    Local $ae_ComboBox1 = UIA_GetAeFirstFromCondition($ae_Pane3, "AutomationID", "comboBoxServerType")
    If @error Then Exit MsgBox(0, 0, "UIA_GetAeFromHandle() error" & @CRLF & "$ae_ComboBox1")
    MsgBox(0, 0, "ComboBox1" & @CRLF & _
            "Name: " & UIA_GetAeCurrentPropertyValue($ae_ComboBox1, "Name") & @CRLF & _
            "ControlType : " & UIA_GetAeCurrentPropertyValue($ae_ComboBox1, "ControlType") & @CRLF & _
            "AutomationID : " & UIA_GetAeCurrentPropertyValue($ae_ComboBox1, "AutomationID"))

EndFunc   ;==>MSSMS_ServerTypeCheckbox_Test

Func UIA_GetAeFromHandle($handle)
    If Not IsObj($UIA_oUIAutomation) Then Return SetError(1)
    If Not WinExists($handle) Then Return SetError(2)

    Local $winPointer
    $UIA_oUIAutomation.ElementFromHandle($handle, $winPointer)
    Local $aeResult = ObjCreateInterface($winPointer, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
    If Not IsObj($aeResult) Then Return SetError(3)

    Return $aeResult
EndFunc   ;==>UIA_GetAeFromHandle

Func UIA_GetAeFirstFromCondition($oAe, $PropertyID, $Value) ;module UIA_PropertyIds
    ; chyba nie działa jeżeli jest tylko jeden child, wtedy użyć UIA_GetAeFirstChild()
    If Not IsObj($UIA_oUIAutomation) Then Return SetError(1)
    If Not IsObj($oAe) Then Return SetError(2)

    $PropertyID = UIA_PropertyNameToID($PropertyID)
    $Value = UIA_ControlTypeNameToID($Value)

    Local $targetPointer, $propCondInterfaceObj
    $UIA_oUIAutomation.CreatePropertyCondition($PropertyID, $Value, $propCondInterfaceObj)
    $oAe.FindFirst($TreeScope_Descendants, $propCondInterfaceObj, $targetPointer)
    Local $aeResult = ObjCreateInterface($targetPointer, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
    If Not IsObj($aeResult) Then Return SetError(3)

    Return $aeResult
EndFunc   ;==>UIA_GetAeFirstFromCondition

Func UIA_PropertyNameToID($PropertyName)
    Local $PropertyID = ""
    Switch $PropertyName
        Case "ControlType"
            $PropertyID = $UIA_ControlTypePropertyId
        Case "AutomationID"
            $PropertyID = $UIA_AutomationIdPropertyId
        Case "Name"
            $PropertyID = $UIA_NamePropertyId
        Case "ClassName"
            $PropertyID = $UIA_ClassNamePropertyId
        Case "BoundingRectangle"
            $PropertyID = $UIA_BoundingRectanglePropertyId
        Case "Handle"
            $PropertyID = $UIA_NativeWindowHandlePropertyId
            ;use Hex() or Hwnd() after
        Case "AccessKey"
            $PropertyID = $UIA_AccessKeyPropertyId
        Case "Value"
            $PropertyID = $UIA_LegacyIAccessibleValuePropertyId
        Case "Value.Value"
            $PropertyID = $UIA_ValueValuePropertyId
        Case "IsInvokePatternAvailable"
            $PropertyID = $UIA_IsInvokePatternAvailablePropertyId
        Case "IsKeyboardFocusable"
            $PropertyID = $UIA_IsKeyboardFocusablePropertyId
        Case "HasKeyboardFocus"
            $PropertyID = $UIA_HasKeyboardFocusPropertyId
    EndSwitch
    If $PropertyID <> "" Then Return $PropertyID
    SetError(1)
    Return $PropertyName
EndFunc   ;==>UIA_PropertyNameToID

Func UIA_GetAeCurrentPropertyValue($oAe, $PropertyID)
    If Not IsObj($UIA_oUIAutomation) Then Return SetError(1)
    If Not IsObj($oAe) Then Return SetError(2)

    $PropertyID = UIA_PropertyNameToID($PropertyID)

    Local $tVal
    $oAe.GetCurrentPropertyValue($PropertyID, $tVal)
    If Not IsArray($tVal) Then Return $tVal
    Local $tStr = $tVal[0]
    For $i = 1 To UBound($tVal) - 1
        $tStr &= "; " & $tVal[$i]
    Next
    Return $tStr
EndFunc   ;==>UIA_GetAeCurrentPropertyValue

Func UIA_ControlTypeNameToID($ControlTypeName)
    Local $ControlTypeID = ""
    Switch $ControlTypeName ;UIA_ControlTypeIds
        Case "Edit"
            $ControlTypeID = $UIA_EditControlTypeId
        Case "Button"
            $ControlTypeID = $UIA_ButtonControlTypeId
        Case "Pane"
            $ControlTypeID = $UIA_PaneControlTypeId
        Case "SplitButton"
            $ControlTypeID = $UIA_SplitButtonControlTypeId
        Case "Toolbar"
            $ControlTypeID = $UIA_ToolBarControlTypeId
        Case "List"
            $ControlTypeID = $UIA_ListControlTypeId
        Case "ListItem"
            $ControlTypeID = $UIA_MenuControlTypeId
        Case "Hyperlink"
            $ControlTypeID = $UIA_HyperlinkControlTypeId
    EndSwitch
    If $ControlTypeID <> "" Then Return $ControlTypeID
    SetError(1)
    Return $ControlTypeName
EndFunc   ;==>UIA_ControlTypeNameToID

 

Link to comment
Share on other sites

  • Solution
Posted (edited)
#include <GuiComboBox.au3>

WinWait('Connect to Server')
$hCtrl = ControlGetHandle('Connect to Server', '', '[REGEXPCLASS:WindowsForms10\.COMBOBOX\.app(.*?)_ad1; INSTANCE:1]')
$aItem = _GUICtrlComboBox_GetListArray($hCtrl)

If IsArray($aItem) Then
    For $Index = 1 To $aItem[0]
        ConsoleWrite($aItem[$Index] & @CRLF)
    Next
EndIf

; Select new string in combo
_GUICtrlComboBox_SelectString($hCtrl, 'Analysis Services')

Tested with SSMS 20.0.70.0

Edited by Andreik
Link to comment
Share on other sites

1 minute ago, Andreik said:
#include <GuiComboBox.au3>

WinWait('Connect to Server')
$hCtrl = ControlGetHandle('Connect to Server', '', '[REGEXPCLASS:WindowsForms10\.COMBOBOX\.app(.*?)_ad1]')
$aItem = _GUICtrlComboBox_GetListArray($hCtrl)

If IsArray($aItem) Then
    For $Index = 1 To $aItem[0]
        ConsoleWrite($aItem[$Index] & @CRLF)
    Next
EndIf

; Select new string in combo
_GUICtrlComboBox_SelectString($hCtrl, 'Analysis Services')

Tested with SSMS 20.0.70.0

no way 😅

Link to comment
Share on other sites

Do I understand correctly? That this code sets the appropriate text in the control but it doesn't necessarily make a selection? The issue is that the text in the next combobox depends on the value chosen in the first control, and while this code does set the text in the Server type to the chosen one, it doesn't actually make the selection, hence the subsequent combobox cannot choose the appropriate text as it doesn't change to match the selection from the first one.

Link to comment
Share on other sites

You can make a selection with _GUICtrlComboBox_SetCurSel() but this doesn't mean that the UI will change because this is done internally by SSMS. Basically if you want to see the change in UI you can do something like this:

#include <GuiComboBox.au3>

Local $hWin = WinWait('Connect to Server')
Local $hCtrl = ControlGetHandle('Connect to Server', '', '[REGEXPCLASS:WindowsForms10\.COMBOBOX\.app(.*?)_ad1; INSTANCE:1]')
Local $aItem = _GUICtrlComboBox_GetListArray($hCtrl)
Local $sText

If IsArray($aItem) Then
    For $Index = 1 To $aItem[0]
        ControlSend($hWin, '', $hCtrl, '{DOWN}')
        _GUICtrlComboBox_GetLBText($hCtrl, _GUICtrlComboBox_GetCurSel($hCtrl), $sText)
        If $sText = 'Analysis Services' Then ExitLoop
    Next
EndIf

 

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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...