Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IAccessible with Google Chrome returns incomplete tree

I'm trying to create a QA automation for Google Chrome, in order to simulate clicks and receive click events on the tab buttons.

I'm using the IAccessible interface and AccessibleChildren API to get the full accessible elements tree.

When using AccExplorer 2.0 to view the tree - it looks great (see image at the end).

But my program shows only a partial tree, and the names do not match the ones I see on AccExplorer.

I'm running:

  • Chrome 37 beta
  • Win 7 64 bit

Any ideas why I can't view the full tree?

Thanks

This is my program's source (C++) : (similar to MSDN example)

int _tmain(int argc, _TCHAR* argv[])
{
    int i=0;
    HWND hWndChrome = NULL;
    hWndChrome = (HWND)0x000702c0;
    wcout << L"\n\nChrome_WidgetWin_1 = "<< hex << hWndChrome << "\n--------------------------";

    CComPtr<IAccessible> pAccMain;
    ::AccessibleObjectFromWindow(hWndChrome, OBJID_CLIENT, IID_IAccessible, (void**)(&pAccMain));

    WalkTreeWithAccessibleChildren(pAccMain, 0);
    _getch();

    return 0;
}

HRESULT WalkTreeWithAccessibleChildren(__in CComPtr<IAccessible> pAcc, __in int depth)
{
    long childCount = 0;
    long returnCount = 0;

    HRESULT hr = pAcc->get_accChildCount(&childCount);

    if (childCount == 0)
        return S_FALSE;

    CComVariant* pArray = new CComVariant[childCount];
    hr = ::AccessibleChildren(pAcc, 0L, childCount, pArray, &returnCount);
    if (FAILED(hr))
        return hr;

    // Iterate through children.
    for (int x = 0; x < returnCount; x++)
    {
        CComVariant vtChild = pArray[x];
        // If it's an accessible object, get the IAccessible, and recurse.
        if (vtChild.vt == VT_DISPATCH)
        {
            CComPtr<IDispatch> pDisp = vtChild.pdispVal;
            CComQIPtr<IAccessible> pAccChild = pDisp;
            if (!pAccChild)
                continue;

            // Print current accessible element
            wcout << endl;
            for (int y = 0; y < depth; y++) wcout << L"    ";
            wcout << L"* " << GetName(pAccChild, CHILDID_SELF).data() << L"  |  " << GetRole(pAccChild, CHILDID_SELF).data() << L" (Object)";

            WalkTreeWithAccessibleChildren(pAccChild, depth + 1);       
        }
        // Else it's a child element so we have to call accNavigate on the parent,
        //   and we do not recurse because child elements can't have children.
        else
        {
            // Print current accessible element
            wcout << endl;
            for (int y = 0; y < depth; y++) wcout << L"    ";
            wcout << L"* " << GetName(pAcc, vtChild.lVal).data() << L"  |  " << GetRole(pAcc, vtChild.lVal).data() << " (Child)";
        }
    }
    delete[] pArray;
    return S_OK;
}

wstring GetName(__in CComPtr<IAccessible> pAcc, __in CComVariant varChild)
{
    if (!pAcc)
        return L"";
    CComBSTR bstrName;
    HRESULT hr = pAcc->get_accName(varChild, &bstrName);
    if (FAILED(hr))
        return L"";
    if (!bstrName.m_str)
        return L"<NULL>";
    return bstrName.m_str;
}

wstring GetRole(__in CComPtr<IAccessible> pAcc, __in CComVariant varChild)
{
    if (!pAcc)
        return L"";
    CComVariant varRoleID;
    HRESULT hr = pAcc->get_accRole(varChild, &varRoleID);
    if (FAILED(hr))
        return L"";
    WCHAR sRoleBuff[1024] = {0};
    hr = ::GetRoleText(varRoleID.lVal, sRoleBuff, 1024);
    if (FAILED(hr))
        return L"";
    return sRoleBuff;
}

And this is the console output:

    Chrome_WidgetWin_1 = 000702C0
    --------------------------
    * Chrome Legacy Window  |  window (Object)
        * System  |  menu bar (Object)
        * <NULL>  |  title bar (Object)
            * IME  |  push button (Child)
            * Minimize  |  push button (Child)
            * Maximize  |  push button (Child)
            * Context help  |  push button (Child)
            * Close  |  push button (Child)
        * Application  |  menu bar (Object)
        * Chrome Legacy Window  |  client (Object)
    //  -----> { there should be a big sub-tree here } <----- //
        * Vertical  |  scroll bar (Object)
            * Line up  |  push button (Child)
            * Page up  |  push button (Child)
            * Position  |  indicator (Child)
            * Page down  |  push button (Child)
            * Line down  |  push button (Child)
        * Horizontal  |  scroll bar (Object)
            * Column left  |  push button (Child)
            * Page left  |  push button (Child)
            * Position  |  indicator (Child)
            * Page right  |  push button (Child)
            * Column right  |  push button (Child)
        * <NULL>  |  grip (Object)

Here is an AccExplorer screenshot showing the full tree: (some nodes are collapsed) AccExplorer working example


Edit:
When Using OBJID_WINDOW instead OBJID_WINDOW (as in the example, in AccessibleObjectFromWindow) - I get a tree with more nodes, but still I can't see the tab elements.

like image 954
Bagelzone Ha'bonè Avatar asked Aug 17 '14 16:08

Bagelzone Ha'bonè


1 Answers

OK, so after too long, I found the minor little thing that made all the difference...
I forgot to use CoInitialize()...

The interesting thing is that without CoInitialize() it works, but only partially, and no HRESULT indicated this (didn't get anything like CO_E_NOTINITIALIZED).

Anyway - if you use IAccessible wihtout CoInitialize - expect things not to work properly...

like image 136
Bagelzone Ha'bonè Avatar answered Oct 21 '22 03:10

Bagelzone Ha'bonè