Implementación de IAccessibleEx para proveedores
En esta sección se explica cómo agregar funcionalidades del proveedor de automatización de la interfaz de usuario de Microsoft a un servidor de Microsoft Active Accessibility mediante la implementación de la interfaz IAccessibleEx.
Antes de implementar IAccessibleEx, tenga en cuenta los siguientes requisitos:
- La jerarquía de objetos accesibles de Microsoft Active Accessibility de línea base debe estar limpia. IAccessibleEx no puede corregir problemas con las jerarquías de objetos accesibles existentes. Los problemas con la estructura del modelo de objetos deben corregirse en la implementación de Microsoft Active Accessibility antes de implementar IAccessibleEx.
- La implementación de IAccessibleEx debe cumplir con la especificación de Microsoft Active Accessibility y la especificación de Automatización de la interfaz de usuario. Las herramientas están disponibles para validar el cumplimiento en ambas especificaciones. Para obtener más información, consulte Pruebas de accesibilidad y UI Automation Verify (UIA Verify) Test Automation Framework.
La implementación de IAccessibleEx requiere estos pasos principales:
- Implemente IServiceProvider en el objeto accesible para que la interfaz de IAccessibleEx se pueda encontrar en este o en un objeto independiente.
- Implemente IAccessibleEx en el objeto accesible.
- Cree objetos accesibles para cualquier elemento secundario de Microsoft Active Accessibility, que en Microsoft Active Accessibility se representan mediante la interfaz IAccessible en el objeto primario (por ejemplo, elementos de lista). Implemente IAccessibleEx en estos objetos.
- Implemente IRawElementProviderSimple en todos los objetos accesibles.
- Implemente las interfaces de patrón de control adecuadas en los objetos accesibles.
Implementación de la interfaz IServiceProvider
Dado que la implementación de IAccessibleEx para un control puede residir en un objeto independiente, las aplicaciones cliente no pueden confiar en QueryInterface para obtener esta interfaz. En su lugar, se espera que los clientes llamen a IServiceProvider::QueryService. En la siguiente implementación de ejemplo de este método, se supone que IAccessibleEx no se implementa en un objeto independiente; por lo tanto, el método simplemente llama a través de 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;
}
};
Implementación de la interfaz IAccessibleEx
En Microsoft Active Accessibility, un elemento de interfaz de usuario siempre se identifica mediante una interfaz IAccessible y un identificador secundario. Una sola instancia de IAccessible puede representar varios elementos de la interfaz de usuario.
Dado que cada instancia de IAccessibleEx representa solo un único elemento de interfaz de usuario, cada par de IAccessible e identificador secundario debe asignarse a una sola instancia de IAccessibleEx. IAccessibleEx incluye dos métodos para controlar esta asignación:
- GetObjectForChild: recupera la interfaz de IAccessibleEx del elemento secundario especificado. Este método devuelve NULL si la implementación de IAccessibleEx no reconoce el identificador secundario especificado, no tiene un IAccessibleEx para el elemento secundario especificado o representa un elemento secundario.
- GetIAccessiblePair: recupera la interfaz IAccessible y el identificador secundario del elemento IAccessibleEx. En el caso de las implementaciones de IAccessible que no usan un identificador secundario, este método recupera el objeto IAccessible y el CHILDID_SELF correspondientes.
En el ejemplo siguiente se muestra la implementación de los métodos GetObjectForChild y GetIAccessiblePair para un elemento de una vista de lista personalizada. Los métodos permiten a Automatización de la interfaz de usuario asignar el par de IAccessible e identificador secundario a una instancia de IAccessibleEx correspondiente.
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;
}
}
Si una implementación de objeto accesible no usa un identificador secundario, los métodos todavía se pueden implementar como se muestra en el siguiente fragmento de código.
// 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;
}
Implementación de la interfaz IRawElementProviderSimple
Los servidores usan IRawElementProviderSimple para exponer información sobre las propiedades y los patrones de control de Automatización de la interfaz de usuario. IRawElementProviderSimple incluye los métodos siguientes:
- GetPatternProvider: este método se usa para exponer interfaces de patrón de control. Devuelve un objeto que admite el patrón de control especificado o NULL si no se admite el patrón de control.
- GetPropertyValue: este método se usa para exponer valores de propiedad de Automatización de la interfaz de usuario.
- HostRawElementProvider: este método no se usa con implementaciones de IAccessibleEx.
- ProviderOptions: este método no se usa con implementaciones de IAccessibleEx.
Un servidor IAccessibleEx expone patrones de control mediante la implementación de IRawElementProviderSimple::GetPatternProvider. Este método toma un parámetro entero que especifica el patrón de control. El servidor devuelve NULL si no se admite el patrón. Si se admite la interfaz de patrón de control, los servidores devuelven un IUnknown y el cliente, a continuación, llama a QueryInterface para obtener el patrón de control adecuado.
Un servidor IAccessibleEx puede admitir propiedades de Automatización de la interfaz de usuario (como LabeledBy e IsRequiredForForm) implementando IRawElementProviderSimple::GetPropertyValue y proporcionando un PROPERTYID entero que identifica la propiedad como parámetro. Esta técnica solo se aplica a propiedades de Automatización de la interfaz de usuario que no se incluyen en una interfaz de patrón de control. Las propiedades asociadas a una interfaz de patrón de control se exponen a través del método de interfaz de patrón de control. Por ejemplo, la propiedad IsSelected del patrón de control SelectionItem se expondría con ISelectionItemProvider::get_IsSelected.