Implementar um provedor de Server-Side Automação da Interface do Usuário

Este tópico descreve como implementar um provedor microsoft Automação da Interface do Usuário do lado do servidor para um controle personalizado escrito em C++. Ele contém as seções a seguir:

Para obter exemplos de código que mostram como implementar provedores do lado do servidor, consulte Tópicos de instruções para provedores de Automação da Interface do Usuário.

Estrutura da árvore do provedor

Você deve implementar um provedor UIA para cada elemento de interface do usuário que precisa ser acessível a um cliente UIA.

Por exemplo, cada elemento deve implementar IRawElementProviderFragment enquanto o elemento raiz do aplicativo deve implementar IRawElementProviderFragmentRoot. Além disso, cada elemento de provedor deve vincular a um:

  • pai
  • elemento de provedor anterior
  • próximo elemento de provedor
  • primeiro provedor filho
  • filho do último provedor

Interfaces do provedor

As interfaces COM (Component Object Model) a seguir fornecem funcionalidade para controles personalizados. Para fornecer funcionalidade básica, cada provedor de Automação da Interface do Usuário deve implementar pelo menos a interface IRawElementProviderSimple. As interfaces IRawElementProviderFragment e IRawElementProviderFragmentRoot são opcionais, mas devem ser implementadas para elementos em um controle complexo para fornecer funcionalidade adicional.

Interface Descrição
Irawelementprovidersimple Fornece funcionalidade básica para um controle hospedado em uma janela, incluindo suporte para padrões de controle e propriedades.
Irawelementproviderfragment Adiciona funcionalidade para um elemento em um controle complexo, incluindo navegar no fragmento, definir o foco e retornar o retângulo delimitador do elemento.
Irawelementproviderfragmentroot Adiciona funcionalidade ao elemento raiz em um controle complexo, incluindo a localização de um elemento filho em coordenadas especificadas e a definição do estado de foco para todo o controle.

 

Observação

Na API Automação da Interface do Usuário para código gerenciado, essas interfaces formam uma hierarquia de herança. Esse não é o caso em C++, em que as interfaces são completamente separadas.

 

As interfaces a seguir fornecem funcionalidade adicional, mas a implementação é opcional.

Interface Descrição
IRawElementProviderAdviseEvents Permite que o provedor acompanhe as solicitações de eventos.
IRawElementProviderHwndOverride Habilita o reposicionamento de elementos baseados em janela na árvore Automação da Interface do Usuário de um fragmento.

 

Funcionalidade necessária para provedores de Automação da Interface do Usuário

Para se comunicar com Automação da Interface do Usuário, seu controle deve implementar as main áreas de funcionalidade descritas na tabela a seguir.

Funcionalidade Implementação
Exponha o provedor a Automação da Interface do Usuário. Em resposta a uma mensagem WM_GETOBJECT enviada à janela de controle, retorne o objeto que implementa IRawElementProviderSimple. Para fragmentos, esse deve ser o provedor da raiz do fragmento.
Forneça valores de propriedade. Implemente IRawElementProviderSimple::GetPropertyValue para fornecer ou substituir valores.
Habilite o cliente para interagir com o controle . Implemente interfaces que dão suporte a cada padrão de controle apropriado, como IInvokeProvider. Retorne esses provedores de padrão de controle em sua implementação de IRawElementProviderSimple::GetPatternProvider.
Gerar eventos. UiaRaiseAutomationEvent, métodos de IProxyProviderWinEventSink.
Habilite a navegação e o foco em um fragmento. Implemente IRawElementProviderFragment para cada elemento dentro do fragmento. Não é necessário para elementos que não fazem parte de um fragmento.
Habilite o foco e a localização de elementos filho em um fragmento. Implemente IRawElementProviderFragmentRoot. Não é necessário para elementos que não são raízes de fragmento.

 

Valores da propriedade

Automação da Interface do Usuário provedores de controles personalizados devem dar suporte a determinadas propriedades que podem ser usadas por Automação da Interface do Usuário e por aplicativos cliente. Para elementos hospedados em janelas, Automação da Interface do Usuário pode recuperar algumas propriedades do provedor de janela padrão, mas deve obter outras do provedor personalizado.

Normalmente, os provedores para controles baseados em janela não precisam fornecer as seguintes propriedades identificadas por PROPERTYID:

A propriedade RuntimeId de um elemento simples ou raiz de fragmento hospedada em uma janela é obtida da janela. No entanto, os elementos de fragmento abaixo da raiz, como itens de lista em uma caixa de listagem, devem fornecer seus próprios identificadores. Para obter mais informações, consulte IRawElementProviderFragment::GetRuntimeId.

A propriedade IsKeyboardFocusable deve ser retornada para provedores hospedados em um controle Windows Forms. Nesse caso, talvez o provedor de janela padrão não seja capaz de recuperar o valor correto.

A propriedade Name geralmente é fornecida pelo provedor de host.

Eventos de provedores

Provedores de Automação da interface do usuário devem gerar eventos para notificar os aplicativos cliente de alterações no estado da interface do usuário. As funções a seguir são usadas para gerar eventos.

Função Descrição
UiaRaiseAutomationEvent Gera vários eventos, incluindo eventos disparados por padrões de controle.
UiaRaiseAutomationPropertyChangedEvent Gera um evento quando uma propriedade de Automação da interface do usuário tiver sido alterada.
UiaRaiseStructureChangedEvent Gera um evento quando a estrutura da árvore Automação da Interface do Usuário é alterada, por exemplo, removendo ou adicionando um elemento.

 

A finalidade de um evento é notificar o cliente de algo que está ocorrendo na interface do usuário. Os provedores devem gerar um evento independentemente de a alteração ter sido disparada pela entrada do usuário ou por um aplicativo cliente usando Automação da Interface do Usuário. Por exemplo, o evento identificado por UIA_Invoke_InvokedEventId deve ser acionado sempre que o controle é invocado, seja por meio de entrada direta do usuário ou pelo aplicativo cliente que chama IUIAutomationInvokePattern::Invoke.

Para otimizar o desempenho, um provedor pode gerar eventos seletivamente ou não gerar eventos se nenhum aplicativo cliente estiver registrado para recebê-los. Os elementos de API a seguir são usados para otimização.

Elemento API Descrição
UiaClientsAreListening Essa função verifica se algum aplicativo cliente se inscreveu para Automação da Interface do Usuário eventos.
IRawElementProviderAdviseEvents Implementar essa interface em uma raiz de fragmento permite que o provedor seja avisado quando os clientes registrarem e cancelarem o registro de manipuladores de eventos no fragmento.

 

Observação

Semelhante à implementação da contagem de referência na programação COM, é importante que Automação da Interface do Usuário provedores tratem os métodos IRawElementProviderAdviseEvents::AdviseEventAdded e AdviseEventRemoved, como os métodos IUnknown::AddRef e Release da interface IUnknown. Desde que AdviseEventAdded tenha sido chamado mais vezes do que AdviseEventRemoved para um evento ou propriedade específico, o provedor deve continuar a gerar eventos correspondentes, pois alguns clientes ainda estão escutando. Como alternativa, Automação da Interface do Usuário provedores podem usar a função UiaClientsAreListening para determinar se pelo menos um cliente está escutando e, nesse caso, gerar todos os eventos apropriados.

 

Navegação do provedor

Os provedores de controles simples, como um botão personalizado hospedado em uma janela, não precisam dar suporte à navegação na árvore Automação da Interface do Usuário. A navegação de e para o elemento é tratada pelo provedor padrão para a janela do host, que é especificada na implementação de IRawElementProviderSimple::HostRawElementProvider. No entanto, quando você implementa um provedor para um controle personalizado complexo, deve dar suporte à navegação entre o nó raiz do fragmento e seus descendentes e entre nós irmãos.

Observação

Elementos de um fragmento diferente da raiz devem retornar NULL de HostRawElementProvider, pois não estão hospedados diretamente em uma janela e nenhum provedor padrão pode dar suporte à navegação de e para eles.

 

A estrutura do fragmento é determinada pela implementação de IRawElementProviderFragment::Navigate. Para cada direção possível de cada fragmento, esse método retorna o objeto de provedor para o elemento nessa direção. Se não houver nenhum elemento nessa direção, o método retornará NULL.

A raiz do fragmento dá suporte à navegação somente para elementos filho. Por exemplo, uma caixa de listagem retorna o primeiro item na lista quando a direção é NavigateDirection_FirstChild e retorna o último item quando a direção é NavigateDirection_LastChild. A raiz do fragmento não dá suporte à navegação para um pai ou para irmãos; isso é tratado pelo provedor de janela do host.

Elementos de um fragmento que não são a raiz devem dar suporte à navegação ao pai e a todos os irmãos e filhos que eles têm.

Atribuindo um novo pai

As janelas pop-up são, na verdade, janelas de nível superior e, por padrão, aparecem na árvore Automação da Interface do Usuário como filhos da área de trabalho. Em muitos casos, no entanto, janelas pop-up são logicamente filhas de algum outro controle. Por exemplo, a lista suspensa de uma caixa de combinação é logicamente um filho da caixa de combinação. Da mesma forma, uma janela pop-up de menu é logicamente um filho do menu. Automação da Interface do Usuário fornece suporte para atribuir um novo pai a uma janela pop-up para que ele pareça ser um filho do controle associado.

Para atribuir um novo pai a uma janela pop-up:

  1. Crie um provedor para a janela pop-up. Isso requer que a classe da janela pop-up seja conhecida com antecedência.
  2. Implemente todas as propriedades e padrões de controle como de costume para esse pop-up, como se fosse um controle por si só.
  3. Implemente a propriedade IRawElementProviderSimple::HostRawElementProvider para que ela retorne o valor obtido de UiaHostProviderFromHwnd, em que o parâmetro é o identificador de janela da janela pop-up.
  4. Implemente IRawElementProviderFragment::Navigate para a janela pop-up e seu pai para que a navegação seja tratada corretamente do pai lógico para os filhos lógicos e entre filhos irmãos.

Quando a Automação da interface do usuário encontra a janela pop-up, ela reconhece que a navegação está sendo substituída do padrão e ignora a janela pop-up quando ela é encontrada como um filho da área de trabalho. Em vez disso, o nó só pode ser acessado por meio do fragmento.

Atribuir um novo pai não é adequado para casos em que um controle pode hospedar uma janela de qualquer classe. Por exemplo, um controle rebar pode hospedar qualquer tipo de janela em suas faixas. Para lidar com esses casos, Automação da Interface do Usuário dá suporte a uma forma alternativa de realocação de janela, conforme descrito na próxima seção.

Reposicionamento do provedor

Automação da Interface do Usuário fragmentos podem conter dois ou mais elementos contidos em uma janela. Como cada janela tem seu próprio provedor padrão que considera a janela como um filho de uma janela que contém, o Automação da Interface do Usuário árvore por padrão, mostrará as janelas no fragmento como filhos da janela pai. Na maioria dos casos, esse é um comportamento desejável, mas às vezes pode causar confusão porque não corresponde à estrutura lógica da interface do usuário.

Um bom exemplo disso é um controle rebar. Um controle rebar contém faixas, cada uma das quais, por sua vez, pode conter um controle baseado em janela, como uma barra de ferramentas, uma caixa de edição ou uma caixa de combinação. O provedor de janela padrão para a janela rebar vê as janelas de controle de banda como crianças e o provedor de versões rebar vê as faixas como crianças. Como o provedor de janelas e o provedor de barras estão trabalhando em conjunto e combinando seus filhos, as faixas e os controles baseados em janela aparecem como filhos do controle rebar. Logicamente, no entanto, somente as faixas devem aparecer como filhos do controle rebar e cada provedor de banda deve ser acoplado com o provedor de janela padrão para o controle que ele contém.

Para fazer isso, o provedor raiz de fragmento para o controle rebar expõe um conjunto de filhos que representam as faixas. Cada banda tem um único provedor que pode expor propriedades e padrões de controle. Em sua implementação de IRawElementProviderSimple::HostRawElementProvider, o provedor de banda retorna o provedor de janela padrão para a janela de controle, que ele obtém chamando UiaHostProviderFromHwnd, passando o identificador de janela do controle (HWND). Por fim, o provedor raiz de fragmento para a barra de rebar implementa a interface IRawElementProviderHwndOverride e, em sua implementação de IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, retorna o provedor de banda apropriado para o controle contido na janela especificada.

Desconectando provedores

Os aplicativos normalmente criam controles conforme são necessários e os destroem posteriormente. Depois de destruir um controle, os recursos do provedor de Automação da Interface do Usuário associados ao controle devem ser liberados chamando o UiaDisconnectProvider.

Da mesma forma, um aplicativo deve usar a função UiaDisconnectAllProviders para liberar todos os recursos Automação da Interface do Usuário mantidos por todos os provedores no aplicativo antes de desligar.

Guia do programador do provedor de Automação da Interface do Usuário