将UI 自动化功能添加到活动辅助功能服务器

没有 Microsoft UI 自动化 提供程序但实现 IAccessible 的控件可以通过实现 IAccessibleEx 接口轻松升级以提供一些UI 自动化功能。 此接口使控件能够公开UI 自动化属性和控件模式,而无需完全实现UI 自动化提供程序接口,例如 IRawElementProviderFragment。 若要实现 IAccessibleEx,基线 Microsoft Active Accessibility 对象层次结构必须不包含错误或不一致 (,例如其父对象未将其列为子) 的子对象,并且不得与UI 自动化规范冲突。 如果 Microsoft Active Accessibility 对象层次结构满足这些要求,则它是使用 IAccessibleEx 添加功能的良好候选项;否则,必须单独或与 Microsoft Active Accessibility 实现一起实现UI 自动化。

以具有范围值的自定义控件为例。 控件的 Microsoft Active Accessibility 服务器定义其角色,并且能够返回其当前值,但缺少返回控件的最小值和最大值的方法,因为这些属性未在 Microsoft Active Accessibility 中定义。 UI 自动化客户端能够检索控件的角色、当前值和其他 Microsoft Active Accessibility 属性,因为UI 自动化核心可以通过 IAccessible 获取这些属性。 但是,如果无法访问对象上的 IRangeValueProvider 接口,UI 自动化也无法检索最大值和最小值。

控件开发人员可以为控件提供完整的UI 自动化提供程序,但这意味着复制 IAccessible 实现的大部分现有功能:例如导航和通用属性。 相反,开发人员可以继续依赖 IAccessible 来提供此功能,同时通过 IRangeValueProvider 添加对特定于控件的属性的支持。

更新自定义控件需要以下main步骤:

  • 在可访问对象上实现 IServiceProvider ,以便可以在此对象或单独的对象上找到 IAccessibleEx 接口。
  • 在可访问对象上实现 IAccessibleEx
  • 为任何 Microsoft Active Accessibility 子项创建不同的可访问对象,这些子项在 Microsoft Active Accessibility 中可能已由父对象上的 IAccessible 接口表示 (例如列表项) 。 在这些对象上实现 IAccessibleEx
  • 在所有可访问对象上实现 IRawElementProviderSimple
  • 在可访问对象上实现适当的控件模式接口。

本主题包含以下各节:

公开 IAccessibleEx

由于控件的 IAccessibleEx 的实现可能驻留在单独的对象中,因此客户端应用程序不能依赖 QueryInterface 来获取此接口。 相反,客户端应调用 IServiceProvider::QueryService。 在此方法的以下示例实现中,假定 IAccessibleEx 未在单独的对象上实现;因此, 方法只是通过 调用 QueryInterface

HRESULT CListboxAccessibleObject::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
{
    if (!ppvObject)
    {
        return E_INVALIDARG;
    }
    *ppvObject = NULL;
    if (guidService == __uuidof(IAccessibleEx))
    {
        return QueryInterface(riid, ppvObject);
    }
    else 
    {
        return E_INVALIDARG;
    }
};

实现 IAccessibleEx

最感兴趣的 IAccessibleEx 方法是 GetObjectForChild。 此方法使 Microsoft Active Accessibility 服务器有机会创建可访问对象 (至少公开子项 的 IAccessibleEx) 的对象。 在 Microsoft Active Accessibility 中,子项通常不表示为辅助对象,而是表示为可访问对象的子项。 但是,由于 UI 自动化 要求每个元素由单独的可访问对象表示,因此 GetObjectForChild 必须按需为每个子元素创建单独的对象。

以下示例实现返回自定义列表视图中项的可访问对象。

HRESULT CListboxAccessibleObject::GetObjectForChild(long idChild, IAccessibleEx **pRetVal)
{ 
    *pRetVal = NULL;
    VARIANT vChild;
    vChild.vt = VT_I4;
    vChild.lVal = idChild;

    // ValidateChildId is an application-defined function that checks whether
    // the child ID is valid. This is similar to code that validates the varChild
    // parameter in IAccessible methods.
    //
    // Additionally, if this idChild corresponds to a child that has its own
    // IAccessible, we should also return E_INVALIDARG here. (The caller
    // should instead be using the IAccessibleEx from that child's own
    // IAccessible in that case.)
    if (idChild == CHILDID_SELF || FAILED(ValidateChildId(vChild)))
    {
        return E_INVALIDARG;
    }

    // Return a suitable provider for this specific child.
    // This implementation returns a new instance each time; an implementation
    // can cache these if desired.

    // _pListboxControl is a member variable pointer to the owning control.
    IAccessibleEx* pAccEx  = new CListItemAccessibleObject(idChild, _pListboxControl);
    if (pAccEx == NULL)
    {
        return E_OUTOFMEMORY;
    }
    *pRetVal = pAccEx;
    return S_OK; 
}

有关完整的示例实现,请参阅 MSDN 上的使自定义控件易于访问,第 5 部分:使用 IAccessibleEx 向自定义控件添加UI 自动化支持

UI 自动化提供程序程序员指南