Implementando IAccessibleEx para provedores

Esta seção explica como adicionar recursos do provedor de Automação da Interface do Usuário a um servidor do Microsoft Active Accessibility implementando a interface IAccessibleEx.

Antes de implementar o IAccessibleEx, considere os seguintes requisitos:

  • A hierarquia de objetos acessíveis do Microsoft Active Accessibility de linha de base precisa estar limpa. IAccessibleEx não pode corrigir problemas com hierarquias de objetos acessíveis existentes. Problemas com a estrutura do modelo de objeto precisam ser corrigidos na implementação do Microsoft Active Accessibility antes de implementar IAccessibleEx.
  • A implementação de IAccessibleEx precisa estar em conformidade com a especificação do Microsoft Active Accessibility e a especificação da Automação da Interface do Usuário. Ferramentas estão disponíveis para validar a conformidade sob ambas as especificações. Para obter mais informações, consulte Testando a acessibilidade e Estrutura de Automação de Teste de Verificação da automação da interface do usuário (UIA Verify).

A implementação do IAccessibleEx exige estas etapas principais:

  • Implemente IServiceProvider no objeto acessível para que a interface IAccessibleEx possa ser encontrada neste objeto ou em um objeto separado.
  • Implemente IAccessibleEx no objeto acessível.
  • Crie objetos acessíveis para qualquer item filho do Microsoft Active Accessibility, que no Microsoft Active Accessibility são representados pela interface IAccessible no objeto pai (por exemplo, itens de lista). Implemente IAccessibleEx nesses objetos.
  • Implemente IRawElementProviderSimple em todos os objetos acessíveis.
  • Implemente as interfaces de padrão de controle apropriadas nos objetos acessíveis.

Implementando a interface IServiceProvider

Já que a implementação de IAccessibleEx para um controle pode residir em um objeto separado, os aplicativos cliente não podem depender de QueryInterface para obter essa interface. Em vez disso, espera-se que os clientes chamem IServiceProvider::QueryService. No exemplo de implementação desse método a seguir, presume-se que IAccessibleEx não é implementado em um objeto separado, portanto, o método simplesmente chama 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;
    }
};      

Implementando a interface IAccessibleEx

No Microsoft Active Accessibility, um elemento da interface do usuário é sempre identificado por uma interface IAccessible e uma ID filho. Apenas uma instância de IAccessible pode representar vários elementos da interface do usuário.

Já que cada instância de IAccessibleEx representa apenas um elemento da interface do usuário, cada par IAccessible e ID filho precisa ser mapeado para uma instância de IAccessibleEx. IAccessibleEx inclui dois métodos para manipular esse mapeamento:

  • GetObjectForChild: recupera a interface IAccessibleEx para o filho especificado. Esse método retornará NULL se a implementação de IAccessibleEx não reconhecer a ID filho especificada, não tiver um IAccessibleEx para o filho especificado ou representar um elemento filho.
  • GetIAccessiblePair: recupera a interface IAccessible e a ID filho para o elemento IAccessibleEx. Para implementações de IAccessible que não usam uma ID filho, esse método recupera o objeto IAccessible correspondente e CHILDID_SELF.

O exemplo a seguir mostra a implementação dos métodos GetObjectForChild e GetIAccessiblePair para um item em uma exibição de lista personalizada. Os métodos permitem que a Automação da Interface do Usuário mapeie o par IAccessible e ID filho para uma instância correspondente de 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; 
}
}

Se uma implementação de objeto acessível não usar uma ID filho, os métodos ainda poderão ser implementados conforme mostrado no trecho de código a seguir.

           

    // 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;
    }

Implementar a interface IRawElementProviderSimple

Os servidores usam IRawElementProviderSimple para expor informações sobre propriedades e padrões de controle de Automação da Interface do Usuário. IRawElementProviderSimple inclui os seguintes métodos:

  • GetPatternProvider: esse método é usado para expor interfaces de padrão de controle. Ele retorna um objeto que oferece suporte ao padrão de controle especificado ou NULL se o padrão de controle não é compatível.
  • GetPropertyValue: esse método é usado para expor valores de propriedade de Automação da Interface do Usuário.
  • HostRawElementProvider: esse método não é usado com implementações de IAccessibleEx.
  • ProviderOptions: esse método não é usado com implementações de IAccessibleEx.

Um servidor IAccessibleEx expõe padrões de controle implementando IRawElementProviderSimple::GetPatternProvider. Esse método usa um parâmetro inteiro que especifica o padrão de controle. O servidor retornará NULL se o padrão não for compatível. Se a interface de padrão de controle for compatível, os servidores retornarão um IUnknown e o cliente chamará QueryInterface para obter o padrão de controle apropriado.

Um servidor IAccessibleEx pode dar suporte a propriedades de Automação da Interface do Usuário (como LabeledBy e IsRequiredForForm) implementando IRawElementProviderSimple::GetPropertyValue e fornecendo um PROPERTYID inteiro identificando a propriedade como um parâmetro. Essa técnica se aplica somente às propriedades de Automação da Interface do Usuário que não estão incluídas em uma interface de padrão de controle. As propriedades associadas a uma interface de padrão de controle são expostas por meio do método de interface de padrão de controle. Por exemplo, a propriedade IsSelected do padrão de controle SelectionItem seria exposta com ISelectionItemProvider::get_IsSelected.