实现提供程序的 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。
以下示例演示自定义列表视图中项的 GetObjectForChild 和 GetIAccessiblePair 方法的实现。 这些方法使 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 包括以下方法:
- GetPatternProvider - 此方法用于公开控件模式接口。 它将返回一个对象,该对象支持指定的控件模式,如果控件模式不受支持,则返回 NULL。
- GetPropertyValue - 此方法用于公开 UI 自动化属性值。
- HostRawElementProvider - 此方法需要搭配 IAccessibleEx 实现使用。
- ProviderOptions—此方法需要搭配IAccessibleEx 实现使用。
IAccessibleEx 通过实现 IRawElementProviderSimple::GetPatternProvider 来公开控件模式。 此方法采用指定控件模式的整数参数。 如果模式不受支持,服务器将返回 NULL。 如果支持控制模式接口,服务器将返回 IUnknown,然后客户端调用 QueryInterface 来获取适当的控制模式。
IAccessibleEx 服务器可以通过实现 IRawElementProviderSimple::GetPropertyValue 并提供将属性标识为参数的整数 PROPERTYID 来支持 UI 自动化属性(例如 LabeledBy 和 IsRequiredForForm)。 此方法仅适用于控件模式接口中不包含的 UI 自动化属性。 与控件模式接口关联的属性通过控件模式接口方法公开。 例如,SelectionItem 控件模式中的 IsSelected 属性将通过 ISelectionItemProvider::get_IsSelected 公开。