get_accChildCount不应该返回0

时间:2016-05-26 10:53:24

标签: c++ internet-explorer debugging com ui-automation

我正在尝试从扩展程序和独立应用程序中枚举IE的选项卡。对于其中一个MSAA节点get_accChildCount在从扩展名调用时返回0,而它应该根据inspect和来自独立应用程序的调用返回1.

  • 问题在StackOverflow中被描述为previously,但它是通过一个对我不起作用的黑客来解决的。 /clr/MT不兼容。
  • 还有a topic on MSDN同样的问题。那里没有单一的答案。
  • 如果您使用管理员权限运行IE,则它可以正常运行。
  • API Monitor在一个最小的例子中显示了几千个调用,并且不清楚哪些是相关的。最小的例子如下。

get_accChildCount返回错误的子计数时,有哪些未记录的案例?

在大多数IE版本中,我可以使用哪种方法通过URL激活标签?

#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atltypes.h>
#include <atlsafe.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>

#include <iostream>
#include <string>
#include <vector>
#include <boost/format.hpp>
#include <fstream>
using namespace std;

CComPtr<IAccessible> get_acc_by_hwnd(HWND hwnd) {
    CComPtr<IAccessible> ret;
    HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &ret);
    if (FAILED(hr) || !ret) {
        wcout << L"Accessible::Accessible invalid hwnd" << endl;
    }
    return ret;
}

std::vector<CComPtr<IAccessible>> get_acc_children(CComPtr<IAccessible> acc) {
    std::vector<CComPtr<IAccessible>> ret;
    long count;
    if (FAILED(acc->get_accChildCount(&count))) return ret;
    long count_obtained = 0;
    if (!count) return ret;
    std::vector<CComVariant> accessors(count);
    if (FAILED(::AccessibleChildren(acc, 0, count, &*accessors.begin(), &count_obtained))) return ret;
    accessors.resize(count_obtained);
    for (auto vtChild : accessors) {
        if (vtChild.vt != VT_DISPATCH) continue;
        CComQIPtr<IAccessible> pChild = vtChild.pdispVal;
        if (pChild) ret.push_back(pChild);
    }
    return ret;
}

bool is_client(CComPtr<IAccessible> acc) {
    CComVariant var;
    HRESULT hr = acc->get_accRole(CComVariant(CHILDID_SELF), &var);
    return SUCCEEDED(hr) && var.vt == VT_I4 && var.lVal == 0xA;
}

std::wstring get_descr(CComPtr<IAccessible> acc) {
    CComBSTR str;
    HRESULT hr = acc->get_accDescription(CComVariant(CHILDID_SELF), &str);
    return SUCCEEDED(hr) && str ? std::wstring(str) : L"";
}

int main() {
    ::CoInitialize(nullptr);
    _setmode(_fileno(stdout), _O_U16TEXT);

    // put HWND of the window that contains tab labels
    // it's hardcoded to minimize quantity of API calls
    HWND hwnd = reinterpret_cast<HWND>(0x002D0696);
    CComPtr<IAccessible> iaccessible;
    HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &iaccessible);
    if (FAILED(hr) || !iaccessible) {
        wcout << L"AccessibleBrowser::activate_tab " L"failed to get IAccessible for IE" << endl;
        return EXIT_FAILURE;
    }

    wstring const sentinel = L"\r\n";
    for (auto child : get_acc_children(iaccessible)) if (is_client(child)) {
        for (auto child1 : get_acc_children(child)) { // fails here in extension
            for (auto child2 : get_acc_children(child1)) {
                std::wstring descr = get_descr(child2);
                auto pos = descr.find(sentinel);
                if (pos == string::npos) continue;
                auto tab_url = descr.substr(pos + sentinel.size());
                wcout << tab_url << endl;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

我嘲笑你的节目一段时间没有多少表现。也许我意识到它不应该重现这个问题:(我只能提供一些可能有用的提示,让你在正确的岩石下寻找。

  

然而它通过一个对我不起作用的黑客来解决

这些程序员犯了一个简单的错误,他们忘了打CoInitialize/Ex()。一个非常常见的疏忽。使用/ clr构建选项可以解决该错误,因为它现在是调用它的CLR。您可以轻松地重现此次事故,只需注释掉CoInitialize()调用。不幸的是,它有效地工作了一段时间而没有产生任何大声错误,但你确实得到某些 accobjects的0。您会注意到您的程序不再找到标签。

不太清楚我可以干净地解释这一点,某些COM风格的对象模型实际上并不使用COM基础结构。 DirectX是最好的例子,我们可以将UIAutomation添加到该列表中。我认为当客户端应用程序的组件基于COM时,它会默默地失败。不清楚是不是,DirectUIHWnd完全没有记录。

因此,请停止寻找解决方案,您不会忘记调用CoInitialize(),IE会在激活您的扩展程序之前将其处理。

  

如果您使用管理员权限运行IE,它可以正常运行。

那是一块更好的摇滚乐。许多程序员一直在运行VS,在UIA的情况下角色可能会被颠倒。请记住,您的扩展程序在IE中的沙箱中运行。没有真正的想法提升IE如何影响这一点。在低完整性沙箱中运行的代码中,您无法做到的一件事就是查看以更高完整性模式运行的代码所拥有的UI组件。谷歌“禁用IE增强保护模式”并遵循指南,看看它对你的插件有什么影响。