get_accChildCount在不应该返回 0 时返回 0

get_accChildCount returns 0 when it shouldn't

本文关键字:返回 不应该 accChildCount get      更新时间:2023-10-16

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

  • 这个问题之前在StackOverflow中描述过,但通过一个对我不起作用的破解解决了。/clr/MT不兼容
  • 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"rn";
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;
}
}
}
}

我对你的程序进行了一段时间的研究,但没有太多内容。也许我意识到它不应该重现这个问题太晚了:(我只能提供一些可能有用的提示,让你找到合适的岩石。

但它是通过一个不适用于我的黑客解决的

这些程序员犯了一个简单的错误,他们忘记调用CoInitialize/Ex()。一个非常常见的疏忽。使用/clr build选项可以解决这个错误,因为现在是clr调用它。您可以很容易地重新处理这个错误,只需注释掉CoInitialize()调用即可。不幸的是,它可以工作一段时间,不会产生任何大的错误,但对于某些对象,您确实会得到0。您会注意到您的程序已找不到选项卡。

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

因此,不要再把它当作一种变通方法了,你没有忘记调用CoInitialize(),它将在IE激活你的扩展之前得到处理。

如果您以管理员权限运行IE,它将正常工作。

那是块更好的石头。许多程序员一直运行VS提升,在UIA的情况下,角色可能会颠倒。请记住,您的扩展是在IE内部的沙盒中运行的。不知道提升IE会如何影响这一点。在低完整性沙箱中运行的代码不能做的一件事是,戳一下在高完整性模式中运行的程序所拥有的UI组件。谷歌"禁用IE增强的保护模式",并遵循指南,看看这对你的插件有什么影响。