添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

How can I retrieve the "Description" field of inspect.exe in my pywinauto based automation script?

Ask Question

In my pywinauto script (based on win32 backend), I can retrieve pretty easily the TreeViewWrapper element by looking for the class type and eventually looking at the items text, but some information that I need is only available in the Description field of this element.

I was not able to find a way to retrieve this information.

I tried in UIA mode as well:

But in this case, it does not even appear in the information.

So I tried using the TreeItemWrapper element with the UIA backend in pywinauto, but I could not find the appropriate description not even in the UIAElementInfo. Although something looked pretty similar in the following line:

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

When I call the legacy_properties of the uia_controls.TreeItemWrapper, I get:

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

And in there, the Description is empty.

Can you re-take the screenshot of Inspect.exe in "UI Automation" mode instead of MSAA? Please enable "Show hierarchy" as well. – Vasily Ryabov Oct 25, 2019 at 13:10 I have edited the question as you suggested, I have even used the UIA back to access the element, but I am not able to retrieve the description, even using what seemed to me like the IAccessible::getCurrentDescription... Iam am still not able to get access to it! – Louis Caron Oct 26, 2019 at 12:43 The windows SDK documentation clearly states that this information is not available through UIA: Note The Description property is often used incorrectly and is not supported by Microsoft UI Automation. Microsoft Active Accessibility server developers should not use this property. If more information is needed for accessibility and automation scenarios, use the properties supported by UI Automation elements and control patterns. – Louis Caron Oct 28, 2019 at 7:08 Did you try method .legacy_properties() in pywinauto? It returns a dict of MSAA properties. Don’t recall if “Description” is there. – Vasily Ryabov Oct 28, 2019 at 10:06 Yes description is in there, I have copied the results. But legacy_properties is only available in UIA. – Louis Caron Oct 28, 2019 at 11:22 I was looking for this actually, but I was hoping that there would be some helpers in pywinauto to get this directly. – Louis Caron Oct 25, 2019 at 14:05

Finally, I could not find this possibility through the pywinauto exposed API.

Although pywniauto does expose the Description property through the legacy_properties of the uia_controls.TreeItemWrapper instance, but it returns an empty string. This correlates with the note in teh Windows SDK documentation that states:

Note The Description property is often used incorrectly and is not supported by Microsoft UI Automation. Microsoft Active Accessibility server developers should not use this property. If more information is needed for accessibility and automation scenarios, use the properties supported by UI Automation elements and control patterns.

In the end, I finally developed a small piece of code to search for the element that I need the description of and I could retrieve the Description from there. Here is the code:

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

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.