Implementación de un proveedor de Server-Side Automatización de la interfaz de usuario

En este tema se describe cómo implementar un proveedor de Microsoft Automatización de la interfaz de usuario del lado servidor para un control personalizado escrito en C++. Contiene las secciones siguientes:

Para obtener ejemplos de código que muestran cómo implementar proveedores del lado servidor, vea Temas de procedimientos para proveedores de Automatización de la interfaz de usuario.

Estructura de árbol de proveedor

Debe implementar un proveedor UIA para cada elemento de la interfaz de usuario que necesite ser accesible para un cliente UIA.

Por ejemplo, cada elemento debe implementar IRawElementProviderFragment , mientras que el elemento raíz de la aplicación debe implementar IRawElementProviderFragmentRoot. Además, cada elemento de proveedor debe vincularse a:

  • primario
  • elemento del proveedor anterior
  • elemento de proveedor siguiente
  • primer proveedor secundario
  • último elemento secundario del proveedor

Interfaces de proveedor

Las siguientes interfaces del Modelo de objetos componentes (COM) proporcionan funcionalidad para los controles personalizados. Para proporcionar funcionalidad básica, todos los Automatización de la interfaz de usuario proveedor deben implementar al menos la interfaz IRawElementProviderSimple. Las interfaces IRawElementProviderFragment e IRawElementProviderFragmentRoot son opcionales, pero deben implementarse para los elementos de un control complejo para proporcionar funcionalidad adicional.

Interfaz Descripción
IRawElementProviderSimple Proporciona funcionalidad básica para un control hospedado en una ventana, incluida la compatibilidad con patrones de control y propiedades.
IRawElementProviderFragment Agrega funcionalidad para un elemento de un control complejo, incluida la navegación en el fragmento, el establecimiento del foco y la devolución del rectángulo delimitador del elemento.
IRawElementProviderFragmentRoot Agrega funcionalidad para el elemento raíz de un control complejo, incluida la ubicación de un elemento secundario en las coordenadas especificadas y el establecimiento del estado del foco global del control.

 

Nota

En la API de Automatización de la interfaz de usuario para código administrado, estas interfaces forman una jerarquía de herencia. Este no es el caso en C++, donde las interfaces son completamente independientes.

 

Las interfaces siguientes proporcionan funcionalidad agregada, pero la implementación es opcional.

Interfaz Descripción
IRawElementProviderAdviseEvents Habilita al proveedor para realizar un seguimiento de las solicitudes de eventos.
IRawElementProviderHwndOverride Permite cambiar la posición de los elementos basados en ventanas en el árbol de Automatización de la interfaz de usuario de un fragmento.

 

Funcionalidad necesaria para proveedores de Automatización de la interfaz de usuario

Para comunicarse con Automatización de la interfaz de usuario, el control debe implementar las principales áreas de funcionalidad descritas en la tabla siguiente.

Funcionalidad Implementación
Exponga el proveedor a Automatización de la interfaz de usuario. En respuesta a un mensaje de WM_GETOBJECT enviado a la ventana de control, devuelve el objeto que implementa IRawElementProviderSimple. En el caso de los fragmentos, debe ser el proveedor de las raíces correspondientes.
Proporcione valores de propiedad. Implemente IRawElementProviderSimple::GetPropertyValue para proporcionar o invalidar valores.
Permitir que el cliente interactúe con el control . Implemente interfaces que admitan cada patrón de control adecuado, como IInvokeProvider. Devuelve estos proveedores de patrones de control en la implementación de IRawElementProviderSimple::GetPatternProvider.
Generar eventos. UiaRaiseAutomationEvent, métodos de IProxyProviderWinEventSink.
Habilite la navegación y el enfoque en un fragmento. Implemente IRawElementProviderFragment para cada elemento dentro del fragmento. No es necesario para los elementos que no forman parte de un fragmento.
Habilite el enfoque y la localización de elementos secundarios en un fragmento. Implemente IRawElementProviderFragmentRoot. No es necesario para los elementos que no son raíces de fragmento.

 

Valores de propiedad

Automatización de la interfaz de usuario proveedores de controles personalizados deben admitir determinadas propiedades que las aplicaciones cliente pueden usar Automatización de la interfaz de usuario y . En el caso de los elementos hospedados en ventanas, Automatización de la interfaz de usuario pueden recuperar algunas propiedades del proveedor de ventanas predeterminado, pero deben obtener otras del proveedor personalizado.

Normalmente, los proveedores de controles basados en ventanas no necesitan proporcionar las siguientes propiedades identificadas por PROPERTYID:

La propiedad RuntimeId de un elemento simple o raíz de fragmento hospedada en una ventana se obtiene de la ventana. Sin embargo, los elementos de fragmento debajo de la raíz, como los elementos de lista de un cuadro de lista, deben proporcionar sus propios identificadores. Para obtener más información, vea IRawElementProviderFragment::GetRuntimeId.

La propiedad IsKeyboardFocusable debe devolverse para los proveedores hospedados en un control Windows Forms. En este caso, es posible que el proveedor de ventana predeterminado no consiga recuperar el valor correcto.

Normalmente, el proveedor de host proporciona la propiedad Name.

Eventos de proveedores

Los proveedores de Automatización de la interfaz de usuario deben generar eventos para notificar a las aplicaciones cliente los cambios en el estado de la interfaz de usuario. Las funciones siguientes se usan para generar eventos.

Función Descripción
UiaRaiseAutomationEvent Genera varios eventos, incluidos los que desencadenan los patrones de control.
UiaRaiseAutomationPropertyChangedEvent Genera un evento cuando una propiedad de Automatización de la interfaz de usuario ha cambiado.
UiaRaiseStructureChangedEvent Provoca un evento cuando la estructura del árbol de Automatización de la interfaz de usuario ha cambiado, por ejemplo, quitando o agregando un elemento.

 

El propósito de un evento es notificar al cliente que algo tiene lugar en la interfaz de usuario. Los proveedores deben generar un evento independientemente de si el cambio se desencadenó por la entrada del usuario o por una aplicación cliente mediante Automatización de la interfaz de usuario. Por ejemplo, el evento identificado por UIA_Invoke_InvokedEventId debe generarse siempre que se invoque el control, ya sea a través de la entrada directa del usuario o mediante la aplicación cliente que llama a IUIAutomationInvokePattern::Invoke.

Para optimizar el rendimiento, un proveedor puede generar eventos de forma selectiva o no generarlos en absoluto si no se registra ninguna aplicación cliente para recibirlos. Los siguientes elementos de API se usan para la optimización.

Elemento API Descripción
UiaClientsAreListening Esta función determina si alguna aplicación cliente se ha suscrito a eventos de Automatización de la interfaz de usuario.
IRawElementProviderAdviseEvents La implementación de esta interfaz en una raíz de fragmento permite que el proveedor se recomiende cuando los clientes registren y anulen el registro de controladores de eventos para los eventos del fragmento.

 

Nota

De forma similar a la implementación del recuento de referencias en la programación COM, es importante que los proveedores de Automatización de la interfaz de usuario traten los métodos IRawElementProviderAdviseEvents::AdviseEventAdded y AdviseEventRemoved, como los métodos IUnknown::AddRef y Release de la interfaz IUnknown. Siempre que se haya llamado a AdviseEventAdded más veces que AdviseEventRemoved para un evento o propiedad específicos, el proveedor debe seguir generando eventos correspondientes, ya que algunos clientes siguen escuchando. Como alternativa, los proveedores de Automatización de la interfaz de usuario pueden usar la función UiaClientsAreListening para determinar si al menos un cliente está escuchando y, si es así, generar todos los eventos adecuados.

 

Navegación del proveedor

Los proveedores de controles simples, como un botón personalizado hospedado en una ventana, no necesitan admitir la navegación en el árbol de Automatización de la interfaz de usuario. El proveedor predeterminado controla la navegación hacia y desde el elemento para la ventana host, que se especifica en la implementación de IRawElementProviderSimple::HostRawElementProvider. Sin embargo, cuando se implementa un proveedor para un control personalizado complejo, sí se debe admitir la navegación entre el nodo raíz del fragmento y sus descendientes, y entre los nodos del mismo nivel.

Nota

Los elementos de un fragmento distinto de la raíz deben devolver NULL de HostRawElementProvider, ya que no están hospedados directamente en una ventana y ningún proveedor predeterminado puede admitir la navegación hacia y desde ellos.

 

La estructura del fragmento viene determinada por la implementación de IRawElementProviderFragment::Navigate. Este método devuelve el objeto de proveedor para el elemento en cada dirección posible, desde cada fragmento. Si no hay ningún elemento en esa dirección, el método devuelve NULL.

La raíz de fragmento solo permite navegar hacia los elementos secundarios. Por ejemplo, un cuadro de lista devuelve el primer elemento de la lista cuando se NavigateDirection_FirstChild la dirección y devuelve el último elemento cuando la dirección es NavigateDirection_LastChild. La raíz del fragmento no admite la navegación a un elemento primario o a elementos del mismo nivel; Esto se controla mediante el proveedor de ventanas host.

Los elementos de un fragmento que no son la raíz deben admitir la navegación hacia el elemento primario, sus elementos secundarios y los elementos del mismo nivel que estos contengan.

Asignación de un nuevo elemento primario

Las ventanas emergentes son realmente ventanas de nivel superior y, de forma predeterminada, aparecen en el árbol de Automatización de la interfaz de usuario como elementos secundarios del escritorio. No obstante, en muchos casos, las ventanas emergentes son, lógicamente, elementos secundarios de otros controles. Por ejemplo, la lista desplegable de un cuadro combinado es, lógicamente, un elemento secundario de dicho cuadro. De forma similar, una ventana emergente de menú es, lógicamente, un elemento secundario de un menú. Automatización de la interfaz de usuario proporciona compatibilidad para asignar un nuevo elemento primario a una ventana emergente para que parezca ser un elemento secundario del control asociado.

Para asignar un nuevo elemento primario a una ventana emergente:

  1. Cree un proveedor para la ventana emergente. Esto requiere que la clase de la ventana emergente se conozca de antemano.
  2. Implemente todas las propiedades y patrones de control como de costumbre para ese elemento emergente, como si fuera un control propio.
  3. Implemente la propiedad IRawElementProviderSimple::HostRawElementProvider para que devuelva el valor obtenido de UiaHostProviderFromHwnd, donde el parámetro es el identificador de ventana de la ventana emergente.
  4. Implemente IRawElementProviderFragment::Navigate para la ventana emergente y su elemento primario para que la navegación se controle correctamente desde el elemento primario lógico a los elementos secundarios lógicos y entre los elementos secundarios del mismo nivel.

Cuando Automatización de la interfaz de usuario encuentra la ventana emergente, reconoce que se está reemplazando la configuración predeterminada de la navegación y omite esta ventana cuando se encuentra como elemento secundario del escritorio. En su lugar, solo se puede acceder al nodo a través del fragmento.

Asignar un nuevo elemento primario no es adecuado para los casos en los que un control puede hospedar una ventana de cualquier clase. Por ejemplo, un control rebar puede hospedar cualquier tipo de ventana en sus bandas. Para controlar estos casos, Automatización de la interfaz de usuario admite una forma alternativa de reubicación de ventanas, como se describe en la sección siguiente.

Cambio de posición del proveedor

Automatización de la interfaz de usuario fragmentos pueden contener dos o más elementos incluidos en una ventana. Dado que cada ventana tiene su propio proveedor predeterminado que considera que la ventana es un elemento secundario de una ventana contenedora, el Automatización de la interfaz de usuario árbol de forma predeterminada mostrará las ventanas en el fragmento como elementos secundarios de la ventana primaria. En la mayoría de los casos este sería el comportamiento deseable, pero a veces puede llevar a confusión porque no coincide con la estructura lógica de la interfaz de usuario.

Un buen ejemplo de esto lo constituye un control rebar. Un control de barra contiene bandas, cada una de las cuales puede contener a su vez un control basado en ventanas, como una barra de herramientas, un cuadro de edición o un cuadro combinado. El proveedor de ventanas predeterminado para la ventana de rebar ve las ventanas de control de banda como elementos secundarios y el proveedor de barras ve las bandas como elementos secundarios. Dado que el proveedor de ventanas y el proveedor de barras funcionan conjuntamente y combinan sus elementos secundarios, las bandas y los controles basados en ventanas aparecen como elementos secundarios del control rebar. Sin embargo, lógicamente, solo las bandas deben aparecer como elementos secundarios del control rebar y cada proveedor de bandas debe acoplarse con el proveedor de ventanas predeterminado para el control que contiene.

Para ello, el proveedor raíz del fragmento para el control de barra de rebar expone un conjunto de elementos secundarios que representan las bandas. Cada banda tiene un único proveedor que puede exponer propiedades y patrones de control. En su implementación de IRawElementProviderSimple::HostRawElementProvider, el proveedor de bandas devuelve el proveedor de ventanas predeterminado para la ventana de control, que obtiene llamando a UiaHostProviderFromHwnd, pasando el identificador de ventana del control (HWND). Por último, el proveedor raíz del fragmento de la barra de rebar implementa la interfaz IRawElementProviderHwndOverride y, en su implementación de IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, devuelve el proveedor de banda adecuado para el control contenido en la ventana especificada.

Desconexión de proveedores

Normalmente, las aplicaciones crean controles a medida que son necesarios y los destruyen después. Después de destruir un control, los recursos del proveedor de Automatización de la interfaz de usuario asociados al control deben liberarse llamando a UiaDisconnectProvider.

Del mismo modo, una aplicación debe usar la función UiaDisconnectAllProviders para liberar todos los recursos Automatización de la interfaz de usuario mantenidos por todos los proveedores de la aplicación antes de apagarse.

Guía del programador del proveedor de Automatización de la interfaz de usuario