2009-09-03 5 views
6

He estado trabajando en inyectar entrada en una aplicación WPF. Lo que hace que este proyecto sea difícil es que necesito poder inyectar la entrada en la aplicación aunque se ejecute en segundo plano (es decir, otra aplicación tiene el foco de entrada). El uso de la función SendInput() está por lo tanto fuera de la cuestión.Inyección de entrada de mouse en aplicaciones WPF

Hasta ahora, tengo la entrada del teclado funcionando pero tengo problemas para inyectar el mouse.

Utilicé Spy ++ para observar los mensajes de la ventana que se envían a la ventana de WPF cuando hago clic físicamente en el botón del mouse. Luego simplemente escribo estos mismos mensajes de mouse (como WM_LBUTTONDOWN y WM_LBUTTONUP) manualmente y los envío explícitamente a la ventana de WPF para emular la entrada del mouse.

Desafortunadamente, esto no funciona como se esperaba (ni siquiera cuando, para fines de prueba, configuré la ventana de WPF como ventana de primer plano).

He añadido un botón a mi ventana de prueba de WPF que, al hacer clic, muestra un cuadro de mensaje. Sin embargo, inyectar los mensajes apropiados del mouse cuando coloqué manualmente el cursor sobre el botón no hace que se haga clic en el botón (es decir, el evento cliqueado no es activado por el marco WPF).

Si agrego un controlador para clics del ratón en el cuadro de diálogo real (el área de cliente), el manejador del hace se llama si se coloco el cursor sobre el propio diálogo e inyectar los mismos mensajes de ventana como antes:

this.MouseLeftButtonDown += WndMouseDown; 

public void WndMouseDown(object sender, EventArgs e) 
{ 
    ... 
} 

Por extraño que parezca, si cambio el modo de presionar el botón para presionar (es decir, se considera presionado el mouse hacia abajo en lugar del mouse predeterminado), el evento de clic se dispara cuando inyecto los mismos mensajes que antes. (Vale la pena mencionar que el controlador del ejemplo anterior dispara correctamente tanto para mouse downs y ups, por lo que parece que el marco WPF procesa ambos mensajes con éxito.)

Parece que hay algunos otros criterios que necesitan cumplirse para que el marco de WPF active un evento con el mouse presionado. ¿Alguien sabe cómo se maneja la entrada del mouse de manera interna en WPF, o por qué no está interpretando los mensajes de mi mouse hacia arriba y hacia abajo como un clic en el botón?

(Vale la pena mencionar que este enfoque [enviar mensajes de la ventana] funciona bien en Windows Win32 normales, como el diálogo Inicio-> Ejecutar. La diferencia aquí es que WPF solo tiene una ventana física Win32 y el resto es específico de WPF , lo que significa que todos los mensajes de ventana ir a la ventana de nivel superior en lugar del botón real.)

he estado buscando altas y bajas para una respuesta a esta y apreciaría cualquier pensamientos o ideas.

+0

Dependiendo de su requerimiento, puede valer la pena mirar Automatización de UI en lugar de intentar simular la entrada. Pero esto dependerá mucho de lo que intentes inyectar * para *: tiende a ser un elemento de control en lugar de cosas a nivel de píxel ... – itowlson

+0

Sí, y hay diferentes niveles de Automatización de UI: http : //blogs.msdn.com/ivo_manolov/archive/2008/12/15/9223397.aspx Ya he analizado el uso de TestAPI para inyectar/automatizar la entrada, pero desafortunadamente usan SendInput() debajo del capó que simplemente inyectar la entrada directamente en el marco de WPF. – dreijer

+0

Lo siento por necroposting, pero ahora con la fuente de referencia de .NET disponible, esto se puede consultar más a fondo https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/ButtonBase.cs , 414 –

Respuesta

1

Use la automatización de IU para hacer esto: intentar simular manualmente la entrada a través de mensajes de ventana es un error de libro de texto, como intentar iniciar una guerra terrestre contra Rusia.

+0

Como dije en mi comentario anterior, eso realmente no funcionará por varias razones. Usando v.g. el marco TestAPI para simular clics del mouse se traduce en SendInput() bajo el capó. Yendo otra ruta para identificar qué control está bajo el cursor y luego invocando ese control explícitamente tiene sus propios problemas debido a patrones de control, que varían para los controles: http://social.msdn.microsoft.com/Forums/en -US/windowsaccessibilityandautomation/thread/25972b31-ac1a-401e-aab3-b7c8b1b96694 # a776adcb-e6d2-43fa-b78c-da6de7628d4e – dreijer

0

Su estrategia es básicamente correcta, pero para enviar un mensaje a una ventana propiedad de otro proceso, primero debe registrar el mensaje.

Here is an article explaining the whole business. El código de muestra desafortunadamente está en VB, pero estoy seguro de que eso no lo detendrá.

+0

Gracias por la entrada (disculpe el juego de palabras :). De hecho, estoy inyectando los mensajes desde el mismo proceso en el mismo hilo de GUI, por lo que no hay problemas de permisos. Como escribí en mi pregunta inicial, los mensajes son procesados ​​correctamente por el marco de WPF a veces (como al hacer clic en el cuadro de diálogo). – dreijer

+0

Sí, acabo de notar la parte de su pregunta en la que menciona que está recibiendo algunos eventos de mouse disparando, lo que implica que todo está en proceso. –

3

Recomiendo encarecidamente la ruta UIAutomation. Crea un AutomationElement por manejador de ventana. Arrástrate al botón e invocalo. Solo me gustaría saber cómo lograste que la entrada del teclado funcionara. Actualmente estoy tratando de resolver el problema inverso. Cómo obtener una ventana de WPF (he logrado obtener un hWnd a través de llamadas de Win32), para responder a los mensajes del teclado virtual. He registrado sesiones de espionaje ++ en la ventana en cuestión y he replicado su entrada sin éxito.

+0

El problema con esto es que no necesariamente sé en qué (tipo de control) está haciendo clic el usuario. Al igual, podría ser un botón, podría ser una barra de desplazamiento, podría ser el control del navegador web. Me está costando visualizar cómo admitir todos estos controles sin ajustar cada tipo. Para ingresar el teclado, asegúrese de que está publicando los mensajes que se deben publicar (es decir, PostMessage() frente a SendMessage()). También puede intentar publicar mientras se ejecuta en el contexto del hilo de la GUI. – dreijer

Cuestiones relacionadas