如何在基于pywinauto的自动化脚本中检索inspect.exe的“描述”字段?

时间:2019-10-25 11:12:08

标签: winapi ui-automation pywinauto

我具有以下SysTreeView32元素,我希望从该元素中检索“描述”字段:

The description bield is highlighted

在我的pywinauto脚本(基于win32后端)中,我可以通过查找类类型并最终查看项目文本来轻松检索TreeViewWrapper元素,但是我需要的一些信息仅在这个元素。

我找不到一种方法来检索此信息。

我也在UIA模式下尝试过

enter image description here

但是在这种情况下,它甚至没有出现在信息中。

因此,我尝试在pywinauto中将TreeItemWrapper元素与UIA后端一起使用,但是即使在UIAElementInfo中也找不到合适的描述。尽管在下面的行中看起来很相似:

impl = uia_defs.get_elem_interface(elem, "LegacyIAccessible").

当我呼叫legacy_properties中的uia_controls.TreeItemWrapper时,我得到:

{'ChildId': 0,
 'DefaultAction': '',
 'Description': '',
 'Help': '',
 'KeyboardShortcut': '',
 'Name': 'Execute multiple tasks(MultiTask_ImportSysD)',
 'Role': 36,
 'State': 3145730,
 'Value': ''}

其中的描述为空。

2 个答案:

答案 0 :(得分:1)

我猜该属性来自IAccessible::get_accDescription

MSDN说该属性已被弃用,但是如果您仍然想使用它,请调用AccessibleObjectFromWindow以获取窗口的IAccessible

答案 1 :(得分:0)

最后,我无法通过pywinauto公开的API找到这种可能性。

尽管pywniauto确实通过legacy_properties实例的uia_controls.TreeItemWrapper公开了Description属性,但是它返回了一个空字符串。这与Windows SDK文档中的注释相关,该注释指出:

  

注意Description属性经常被错误地使用,并且Microsoft UI Automation不支持。 Microsoft Active Accessibility服务器开发人员不应使用此属性。如果可访问性和自动化方案需要更多信息,请使用UI自动化元素和控件模式支持的属性。

最后,我最终开发了一小段代码来搜索需要描述的元素,然后可以从中检索描述。这是代码:

# for OleAcc access
import ctypes
import comtypes, comtypes.automation, comtypes.client

comtypes.client.GetModule('oleacc.dll')

def accDescription(iaccessible, cid):
    objChildId = comtypes.automation.VARIANT()
    objChildId.vt = comtypes.automation.VT_I4
    objChildId.value = cid
    objDescription = comtypes.automation.BSTR()
    iaccessible._IAccessible__com__get_accDescription(objChildId, ctypes.byref(objDescription))
    return objDescription.value

def accRole(iaccessible, cid):
    objChildId = comtypes.automation.VARIANT()
    objChildId.vt = comtypes.automation.VT_I4
    objChildId.value = cid
    objRole = comtypes.automation.VARIANT()
    objRole.vt = comtypes.automation.VT_BSTR
    iaccessible._IAccessible__com__get_accRole(objChildId, objRole)
    return AccRoleNameMap[objRole.value]

def accState(iaccessible, cid):
    '''Get Element State'''
    objChildId = comtypes.automation.VARIANT()
    objChildId.vt = comtypes.automation.VT_I4
    objChildId.value = cid
    objState = comtypes.automation.VARIANT()
    iaccessible._IAccessible__com__get_accState(objChildId, ctypes.byref(objState))
    return objState.value

def accName(iaccessible, cid):
    '''Get Element Name'''
    objChildId = comtypes.automation.VARIANT()
    objChildId.vt = comtypes.automation.VT_I4
    objChildId.value = cid
    objName = comtypes.automation.BSTR()
    iaccessible._IAccessible__com__get_accName(objChildId, ctypes.byref(objName))
    return objName.value

def accDescendants(iaccessible):
    """Iterate all desencendants of an object iaccessible, including the current one.

    Arguments:
    iaccessible -- the IAccessible element to start from

    Yields:
    (IAcessible instance, Child id)
    """
    yield (iaccessible, 0)
    objAccChildArray = (comtypes.automation.VARIANT * iaccessible.accChildCount)()
    objAccChildCount = ctypes.c_long()
    ctypes.oledll.oleacc.AccessibleChildren(iaccessible, 0, iaccessible.accChildCount, objAccChildArray, ctypes.byref(objAccChildCount))
    for i in range(objAccChildCount.value):
        objAccChild = objAccChildArray[i]
        if objAccChild.vt == comtypes.automation.VT_DISPATCH:
            # query the sub element accessible interface
            newiaccessible = objAccChild.value.QueryInterface(comtypes.gen.Accessibility.IAccessible)
            # then loop over its descendants
            for (__i, __c) in accDescendants(newiaccessible):
                yield (__i, __c)
        else: #if objAccChild.vt == comtypes.automation.VT_I4:
            yield (iaccessible, objAccChild.value)


def findObjIAccessible(handle, text):
    """Find the IAccessible based on the name, starting from a specific window handle

    Arguments:
    handle -- the window handle from which to search for the element
    text -- text that should be contained in the name of the IAccessible instance

    Return:
    (None, 0) if not found
    (IAccessible instance, child id) of the first element whose name contains the text
    """
    iacc = ctypes.POINTER(comtypes.gen.Accessibility.IAccessible)()

    ctypes.oledll.oleacc.AccessibleObjectFromWindow(handle, 0, ctypes.byref(comtypes.gen.Accessibility.IAccessible._iid_), ctypes.byref(iacc))

    for (ia, ch) in accDescendants(iacc):
        n = accName(ia, ch)
        if n != None and text in n:
            return (ia, ch)
    else:
        return (None, 0)