You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IAccessible接口调用异常:无法获取子元素名称及入口点错误

问题排查与修复方案

1. 核心错误:AccessibleChildren函数签名不匹配

你当前的AccessibleChildren DllImport签名存在两个致命问题,直接导致"accessibility.dll中找不到入口点"错误:

  • 返回类型错误:AccessibleChildren实际返回HRESULT,当设置PreserveSig = false时,.NET会自动将HRESULT转换为异常,因此返回值应声明为void,而非int。
  • 多余的返回值MarshalAs属性:你添加了[return: MarshalAs(UnmanagedType.Interface)],但该函数的返回值是HRESULT而非接口,这会导致函数签名完全不匹配,系统无法找到正确的入口点。

修复后的正确签名:

[DllImport("oleacc.dll", PreserveSig = false, CharSet = CharSet.Auto)]
public static extern void AccessibleChildren(
    IAccessible paccContainer,
    int iChildStart,
    int cChildren,
    [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct)] object[] rgvarChildren,
    ref int pcObtained);

2. 函数调用不匹配:定义与调用的函数名不一致

你定义的获取子元素的函数名为c,但在GetObjectByName中调用的是GetAccessibleChildren(Parent),这会导致运行时无法找到方法,根本无法正确获取子元素。

修复方案:
将函数名改为GetAccessibleChildren,同时处理AccessibleChildren返回的object[]——因为子元素可能是IAccessible对象,也可能是代表子ID的整数(需要通过accChild属性获取对应的IAccessible):

public static IAccessible[] GetAccessibleChildren(IAccessible accContainer)
{
    if (accContainer == null)
        return new IAccessible[0];

    int childNum = 0;
    try
    {
        childNum = accContainer.accChildCount;
        Console.WriteLine("Child count is " + childNum);
    }
    catch (Exception ex)
    {
        childNum = 0;
        Console.WriteLine("Failed to get child count: " + ex.Message);
    }

    if (childNum == 0)
        return new IAccessible[0];

    object[] rawChildren = new object[childNum];
    int obtainedCount = 0;
    try
    {
        AccessibleChildren(accContainer, 0, childNum, rawChildren, ref obtainedCount);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Failed to retrieve children: " + ex.Message);
        return new IAccessible[0];
    }

    List<IAccessible> accessibleChildren = new List<IAccessible>();
    foreach (var item in rawChildren)
    {
        if (item is IAccessible childAcc)
        {
            accessibleChildren.Add(childAcc);
        }
        else if (item is int childId)
        {
            // 处理子ID对应的可访问对象
            try
            {
                object childObj = accContainer.get_accChild(childId);
                if (childObj is IAccessible accChild)
                {
                    accessibleChildren.Add(accChild);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to access child with ID {childId}: {ex.Message}");
            }
        }
    }

    return accessibleChildren.ToArray();
}

3. 可访问对象名称获取错误

你在GetObjectByName中直接使用Parent.accName[0]和child.accName,存在两个问题:

  • accName是索引器,必须传入子ID(对于当前对象本身,子ID为0),直接访问child.accName会引发错误。
  • 未处理空值和空格问题,导致名称匹配失败。

修复后的名称匹配逻辑:

public static IAccessible GetObjectByName(IAccessible Parent, string Name)
{
    if (Parent == null || !Marshal.IsComObject(Parent))
    {
        return null;
    }

    // 匹配当前对象名称
    string currentName = Parent.get_accName(0)?.Trim();
    if (!string.IsNullOrEmpty(currentName) && currentName.Equals(Name.Trim()))
    {
        return Parent;
    }

    // 递归检查子对象
    IAccessible[] children = GetAccessibleChildren(Parent);
    foreach (IAccessible child in children)
    {
        string childName = child.get_accName(0)?.Trim();
        Console.WriteLine($"Child Name: {childName ?? "null"}");
        
        IAccessible matchedObj = GetObjectByName(child, Name);
        if (matchedObj != null) 
            return matchedObj;
    }

    return null;
}

4. 其他注意事项

  • 确保GetWindowAccessibleByHwnd函数能正确返回顶层IAccessible对象,目标进程的窗口句柄需有效。
  • 部分控件的标识信息可能存储在accValue等其他属性中,可根据实际场景扩展检查逻辑。
  • 运行程序时需具备管理员权限,避免因权限不足无法访问其他进程的可访问对象。

内容的提问来源于stack exchange,提问作者h_curious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:48:23