实现提供程序的 IAccessibleEx

本部分介绍如何通过实现 IAccessibleEx 接口将 Microsoft UI 自动化提供程序功能添加到 Microsoft Active Accessibility 服务器

在实现 IAccessibleEx 之前,请考虑以下要求

  • 基线 Microsoft Active Accessibility 辅助性对象层次结构必须干净。 IAccessibleEx 无法更正现有辅助性对象层次结构的问题。 在实现 IAccessibleEx 之前,必须在 Microsoft Active Accessibility 实现中更正对象模型结构的任何问题
  • IAccessibleEx 实现必须符合 Microsoft Active Accessibility 规范和 UI 自动化规范。 可使用工具来验证这两个规范的合规性。 有关详细信息,请参阅测试辅助功能UI 自动化验证(UIA 验证)测试自动化框架

实现 IAccessibleEx 需要执行以下主要步骤

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

实现 IServiceProvider 接口

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

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

实现 IAccessibleEx 接口

在 Microsoft Active Accessibility 中,UI 元素始终由 IAccessible 接口和子 ID 标识。 IAccessible 的单个实例可以表示多个 UI 元素

由于每个 IAccessibleEx 实例仅表示一个 UI 元素,因此每个 IAccessible 和子 ID 对必须映射到单个 IAccessibleEx 实例。 IAccessibleEx 包括两种处理此映射的方法

  • GetObjectForChild - 检索指定子项的 IAccessibleEx 接口。 如果 IAccessibleEx 实现无法识别指定的子 ID、没有指定子项的 IAccessibleEx,或者表示一个子元素,则此方法将返回 NULL
  • GetIAccessiblePair - 检索 IAccessible 接口和 IAccessibleEx 元素的子 ID。 对于不使用子 ID 的 IAccessible 实现,此方法将检索相应的 IAccessible 对象和 CHILDID_SELF

以下示例演示自定义列表视图中项的 GetObjectForChildGetIAccessiblePair 方法的实现。 这些方法使 UI 自动化能够将 IAccessible 和子 ID 对映射到相应的 IAccessibleEx 实例

           
HRESULT CListboxAccessibleObject::GetObjectForChild(
    long idChild, IAccessibleEx **ppRetVal)
{ 
    VARIANT vChild;
    vChild.vt = VT_I4;
    vChild.lVal = idChild;
    HRESULT hr = ValidateChildId(vChild);
    if (FAILED(hr))
    {
        return E_INVALIDARG;
    }
    // List item accessible objects are stored as an array of
    // pointers; for the purpose of this example it is assumed that 
    // the list contents will not change. Accessible objects are
    // created only when needed.
    if (itemProviders[idChild - 1] == NULL)
    {
        // Create an object that supports UI Automation and
        // IAccessibleEx for the item.
        itemProviders[idChild - 1] = 
          new CListItemAccessibleObject(idChild, 
          g_pListboxControl);
        if (itemProviders[idChild - 1] == NULL)
        {
            return E_OUTOFMEMORY;
        }
    }
    IAccessibleEx* pAccEx = static_cast<IAccessibleEx*>
      (itemProviders[idChild - 1]);
    if (pAccEx != NULL)
    {
      pAccEx->AddRef();
    }
    *ppRetVal = pAccEx;
    return S_OK; 
}

HRESULT CListItemAccessibleObject::GetIAccessiblePair(
    IAccessible **ppAcc, long *pidChild)
{ 
    if (ppAcc == NULL || pidChild == NULL)
    {
        return E_INVALIDARG;   
    }

                CListboxAccessibleObject* pParent = 
        m_control->GetAccessibleObject();

    HRESULT hr = pParent->QueryInterface( 
        __uuidof(IAccessible), (void**)ppAcc);
    if (FAILED(hr))
    {
        *pidChild = 0;
        return E_NOINTERFACE;
    }
    *pidChild = m_childID; 
    return S_OK; 
}
}

如果辅助性对象实现不使用子 ID,仍可实现这些方法,如以下代码片段所示。

           

    // This sample implements IAccessibleEx on the same object; it could use a tear-off
    // or inner object instead.
    class MyAccessibleImpl: public IAccessible,
                        public IAccessibleEx,
                        public IRawElementProviderSimple
    {
    public:
    ...
   HRESULT STDMETHODCALLTYPE GetObjectForChild( long idChild, IAccessibleEx **ppRetVal )
    {
        // This implementation does not support child IDs.
        *ppRetVal = NULL;
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetIAccessiblePair( IAccessible ** ppAcc, long * pidChild )
    {
        // This implementation assumes that IAccessibleEx is implemented on same object as
        // IAccessible.
        *ppAcc = static_cast<IAccessible *>(this);
        (*ppAcc)->AddRef();
        *pidChild = CHILDID_SELF;
        return S_OK;
    }

实现 IRawElementProviderSimple 接口

服务器使用 IRawElementProviderSimple 公开有关 UI 自动化属性和控制模式的信息。 IRawElementProviderSimple 包括以下方法

IAccessibleEx 通过实现 IRawElementProviderSimple::GetPatternProvider 来公开控件模式。 此方法采用指定控件模式的整数参数。 如果模式不受支持,服务器将返回 NULL。 如果支持控制模式接口,服务器将返回 IUnknown,然后客户端调用 QueryInterface 来获取适当的控制模式

IAccessibleEx 服务器可以通过实现 IRawElementProviderSimple::GetPropertyValue 并提供将属性标识为参数的整数 PROPERTYID 来支持 UI 自动化属性(例如 LabeledBy 和 IsRequiredForForm)。 此方法仅适用于控件模式接口中不包含的 UI 自动化属性。 与控件模式接口关联的属性通过控件模式接口方法公开。 例如,SelectionItem 控件模式中的 IsSelected 属性将通过 ISelectionItemProvider::get_IsSelected 公开