Capturar eventos de foco globales

Tus preguntas. Algoritmos o Grupos de Comandos formando Programas Escripts.
Responder
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Capturar eventos de foco globales

Mensaje por Ximorro »

En esta entrada
http://www.emesn.com/autoitforum/viewto ... f=3&t=1809
me resolvió Chefito cómo controlar el foco de mi GUI hecho en AutoIt.

Ahora la consulta es parecida pero más complicada: ¿cómo controlar el foco de todas las ventanas de Windows?
Es decir, por ejemplo que el siguiente código me mostrara qué ventana obtiene y pierde el foco.

Código: Seleccionar todo

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
GuiCreate("Mi GUI de AutoIt")
GuiSetState()

GUIRegisterMsg($WM_SETFOCUS, "WM_SETFOCUS")
GUIRegisterMsg($WM_KILLFOCUS, "WM_KILLFOCUS")

While GUIGetMsg() <> $GUI_EVENT_CLOSE
WEnd

Func WM_SETFOCUS($hWnd, $iMsg, $iwParam, $ilParam)
	ConsoleWrite("WM_SETFOCUS: " & WinGetTitle($hWnd) & @LF)
	Return $GUI_RUNDEFMSG
EndFunc

Func WM_KILLFOCUS($hWnd, $iMsg, $iwParam, $ilParam)
	ConsoleWrite("WM_KILLFOCUS: " & WinGetTitle($hWnd) & @LF)
	Return $GUI_RUNDEFMSG
EndFunc
Pero sólo me muestra los mensajes de mi propio GUI.
En realidad ni siquiera quiero usar un GUI, quiero ver qué ventanas se van activando y desactivando en Windows...

Una manera cutre que se me ocurre es ir haciendo WinGetHandle([active]) continuamente, y si cambia respecto a una llamada anterior pues la anterior ha perdido el foco y la actual lo ha ganado. Pero no mola porque además de usar mucha CPU se puede saltar fácilmente activaciones de ventanas si se producen varias entre llamada y llamada.

¿Hay alguna manera de hacerlo por eventos o similar?, sería genial algo parecido a los WM_xxxFOCUS pero que me notifique todas las ventanas...

Graciaaaaas
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Capturar eventos de foco globales

Mensaje por Chefito »

Yo no lo veo tan chapucero lo que dices. Yo utilizaría la función AdlibRegister, la cual ha sustituído a AdlibEnable. Ahora puedes ejecutar varias funciones al mismo tiempo de forma asíncrona.
Otra forma es como siempre con apis a saco :smt024 :smt024 . Tenemos que utilizar hooks para enguanchar los mensajes de windows. Esto es bastante complicado.
He intentado una forma pero no me ha salido. Por alguna razón se desenguancha.

En el foro ingles he visto esto: http://www.autoitscript.com/forum/index ... ntry545125

He adaptado la función un poquito:

Código: Seleccionar todo

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
Global Const $HSHELL_WINDOWACTIVATED = 4
$hGUI = GUICreate("My Ownerdrawn Created Button", 300, 200)
GUIRegisterMsg(_WinAPI_RegisterWindowMessage("SHELLHOOK"), "FocoDeVentana")
ShellHookWindow($hGui, 1)
GUISetState(0)
$focoWinAnt=WinGetTitle("[active]")
ConsoleWrite("Tiene el foco la ventana: " & $focoWinAnt & @LF&@LF)
While 1
	Sleep(10)
WEnd
Func FocoDeVentana($hWnd, $Msg, $wParam, $lParam)
	ConsoleWrite($wparam)
	If $hshell_windowactivated=$wparam Then ConsoleWrite("Abandonó el foro: " & $focoWinAnt & @LF &"Tiene el foco la ventana: " & WinGetTitle("[active]") & @LF & @LF)
	$focoWinAnt=WinGetTitle("[active]")
	Return $GUI_RUNDEFMSG
EndFunc   
Func ShellHookWindow($hWnd, $bFlag)
	Local $sFunc = 'DeregisterShellHookWindow'
	If $bFlag Then $sFunc = 'RegisterShellHookWindow'
	Local $aRet = DllCall('user32.dll', 'int', $sFunc, 'hwnd', $hWnd)
	Return $aRet[0]
EndFunc
Espero que te sirva de algo.

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Capturar eventos de foco globales

Mensaje por Ximorro »

Jo, ya lo creo que sirve, como la seda. ¡GRACIAS!

Esto de los hooks como no lo conozco no se me ocurre a mí ni de coña. Sí lo prefiero porque los Adlib tienen que estar continuamente mirando, cuando así te notifica el mensaje y sólo actúas en ese momento.

Je je, en vez de "Abandonó el foco" has puesto "Abandonó el foro". Nooooooooooooo, no abandones el foroooooooooooo. :smt003

Una cosa que no entiendo, ¿es necesario tener un GUI para enganchar el Hook? ¿no se puede simplemente asociar al proceso?
Desde luego si quito el ShellHookWindow no va. ¿Por qué no es suficiente el GUIRegisterMsg?

Evidentemente no es vital, se puede tener un GUI oculto, pero me parece una restricción tonta cuando no te hace falta para nada dicho GUI...
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
melvinhn
Hacker del Foro
Mensajes: 75
Registrado: 16 Jul 2011, 22:14
Ubicación: Honduras, Cortes, Puerto Cortes, Col Miraflores
Contactar:

Re: Capturar eventos de foco globales

Mensaje por melvinhn »

En una busqueda encontres este scrip

pero la ayuda que necesito es, cuando el titulo no es el mismo y se pierde podria recibir un nuevo foco

me explico estoy en mi pc, entro a la unidad C: ya la ventana tiene un nuevo titulo, o bien en la unidad c: entro a archivo de programa ya cambia el titulo
hay alguna manera que se actualize, cada vez que vas entrando a una carpeta.
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Capturar eventos de foco globales

Mensaje por Jonny »

Efectivamente, lo suyo es hacerlo con hooks. Yo se algo de estos, pero no los domino al cien por cien si no es mirando la documentación de las apis que los gestionan.

No he probado el código de Chefito, si funciona bien nada. Pero sino, quizá también podrías hacerlo capturando los mensajes del sistema (hooks globales). En autoit en principio no sería posible, pero posteé una dll para poder hacerlo...

El tema de los hooks es bastante complicado pero una vez se domina, da un juego impresionante en los programas.

Salu2!
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Saber titulo de cualquier ventana activa y si cambia éste.

Mensaje por Chefito »

Dios!!! Escribí yo esto? Ni me acordaba :smt005 :smt005 .

Mmmmmm.....programar con apis es algo complicado. Ya ni te cuento manejar hooks :smt012 . Se necesita saber bastante de apis de windows y autoit. Es un nivel bastante avanzado.
Supongo que si hubieses analizado el código y entendido (creo que no lo entiendes), hubieses visto que dejé ConsoleWrite($wparam) dentro de la función que recoge los mensajes de las ventanas. Analizando estos mensajes devueltos, hubieses visto que cuando cambias el título de la ventana salta varias veces el 6 en hexadecimal. Pues ya tienes el mensaje que necesitas :smt003 .

Vale, lo vamos a hacer bien. Vamos a esta página del msdn de microsoft para ver los valores de los códigos hook que nos puede mandar una ventana. Se trata del primer parámetro de la función ShellProc: http://msdn.microsoft.com/en-us/library ... S.85).aspx

Bueno, pues leyendo un poquito y traduciendo mal y pronto, vemos esto que es lo que nos interesa:
HSHELL_REDRAW
6
The title of a window in the task bar has been redrawn.
Dios!!! Sorpresa!!! Coincide con el número 6 del principio!! :smt005 :smt005 .
Pues con esto y un bizcocho ya tenemos la constante que necesitamos.

Un código que te dice cual es el título de la ventana activa y si ha cambiado podría ser algo así:

Código: Seleccionar todo

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>
Global Const $HSHELL_WINDOWACTIVATED = 4
Global Const $HSHELL_REDRAW = 6
Global $tituloAnterior
$hGUI = GUICreate("My Ownerdrawn Created Button", 300, 200)
GUIRegisterMsg(_WinAPI_RegisterWindowMessage("SHELLHOOK"), "FocoDeVentana")
ShellHookWindow($hGui, 1)
GUISetState(0)
$tituloAnterior=WinGetTitle("[active]")
ConsoleWrite(">>La ventana activa es: " & $tituloAnterior & @LF&@LF)
While 1
   Sleep(10)
WEnd
Func FocoDeVentana($hWnd, $Msg, $wParam, $lParam)
;~    ConsoleWrite($wparam)
	Switch $wParam
		Case $HSHELL_REDRAW=$wparam
		   $tituloVentana=WinGetTitle("[active]")
		   If $tituloVentana<>$tituloAnterior Then
			   $tituloAnterior=$tituloVentana	;con esto evitamos que se repita varias veces el consolewrite, ya que el mismo mensaje se lanza varias veces.
				ConsoleWrite("<<El título de la ventana ha cambiado. Ahora es: " & $tituloVentana & @LF & @LF)
			EndIf
		Case $HSHELL_WINDOWACTIVATED
			ConsoleWrite(">>La ventana activa es: " & WinGetTitle("[active]") & @LF&@LF)
	EndSwitch
   Return $GUI_RUNDEFMSG
EndFunc
Func ShellHookWindow($hWnd, $bFlag)
   Local $sFunc = 'DeregisterShellHookWindow'
   If $bFlag Then $sFunc = 'RegisterShellHookWindow'
   Local $aRet = DllCall('user32.dll', 'int', $sFunc, 'hwnd', $hWnd)
   Return $aRet[0]
EndFunc
Como habrás comprobado dispones de varios mensajes que envían las ventanas en la página del mdsn que te pasé antes. Puedes capturarlos y hacer que haga lo que quieras añadiendolos al switch.

Un dato curioso que se me acaba de ocurrir ahora mismo. Por ejemplo, sabiendo el handle de una ventana específica a la cual le quisieses capturar cualquier tipo de mensajes, podrías utilizar la api getmessage. Tendrías que implementarla (y sus estructuras) y hacer alguna otra cosilla que seguro que es complicada :smt001 .

Tiene que haber muchas más formas de hacer cosas de estas. Las apis de win son un mundo. Te puedes volver loco :smt005 :smt021 .

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Capturar eventos de foco globales

Mensaje por Ximorro »

melvinhn, otra manera de no tener problemas con los cambios de título de ventana es usar su identificador único de Windows, en vez del título. Es lo que llamamos "handler" o manejador.

Así en vez de $titu=WinGetTitle("[active]"), que te da directamente el título, puedes hacer $hWnd=WinGetHandle("[active]").
Lo bueno de este método es que aunque cambie el título de una ventana ¡su manejador sigue siendo el mismo!

Así que puedes usar el esquema de Chefito pero mirando si cambia el manejador, en vez del título, de esa manera cuando una ventana cambie de título pero no de foco lo detectarás.
Además en cualquier momento puedes saber el título de esa ventana con WinGetTitle($hWnd).
De la misma manera, si tienes un título puedes obtener su manejador con WinGetHandle($titu)
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
ms999
Hacker del Foro
Mensajes: 116
Registrado: 26 Ene 2011, 06:13

Re: Capturar eventos de foco globales

Mensaje por ms999 »

Y otra manera de conseguir el handle es usando la clase

$WinHandle = WinGetHandle("[CLASS:CabinetWClass]")

Yo suelo usar este metodo, casi nunca uso el titulo de la ventana, quiza si se que habran varias ventantas abiertas de lo mismo.
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Capturar eventos de foco globales

Mensaje por Ximorro »

Bueno, es la misma, la función WinGetHandle :smt002

Pero no entiendo lo que quieres decir. Esto era para tomar la ventana con el foco, por eso usamos "[active]", si pones una clase específica pues sólo sirve para esa, que además puede no tener el foco...
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Capturar eventos de foco globales

Mensaje por Chefito »

Mmmmm....ahora que lo pienso y leo a los compañeros....puede que te haya entendido mal y tu hayas buscado un mal código para hacer algo. Si lo que quieres es trabajar con una ventana, y la estás identificando por su título, y éste cambia perdiendo la posibilidad de seguir trabajando con la ventana, haz caso a los compas e inicialmente saca su handle (identificador de ventana) con wingethandle. Este nunca cambia. Ya te explican los compañeros como hacerlo. Si es esto, olvida mi código que no tiene nada que ver.

Como te referiste a este post, yo pensaba que necesitabas saber de manera asíncrona cuando cambiaba el título de cualquier ventana :smt012 .

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Avatar de Usuario
melvinhn
Hacker del Foro
Mensajes: 75
Registrado: 16 Jul 2011, 22:14
Ubicación: Honduras, Cortes, Puerto Cortes, Col Miraflores
Contactar:

Re: Capturar eventos de foco globales

Mensaje por melvinhn »

Pues mira en lo que cabe me ha ayudado el codigo
pero ahora me presenta la siguiente inquietud

Código: Seleccionar todo

While(1)
HotKeySet("^!v", "TituloVentana")
Sleep(100)
WEnd

Func TituloVentana()
Switch $TituloVentana = "("&@UserName&") "&WinGetTitle("[active]")
If $TituloVentana < > $TituloAnterior Then
$TituloAnterior = $TituloVentana   ;con esto evitamos que se repita varias veces el consolewrite, ya que el mismo mensaje se lanza varias veces.
ConsoleWrite(@CRLF&$TituloVentana&@CRLF)
EndIf
EndSwitch
EndFunc
Quiero captuara el titulo de la ventana al precinar la combinacion de teclas Control + Alt + V

pero no quiero que el titulo se repita... si yo aprieto la combinacion de teclas dos veces no quiero que se repita el titulo.

bueno de antemano gracias... :smt017
Avatar de Usuario
ms999
Hacker del Foro
Mensajes: 116
Registrado: 26 Ene 2011, 06:13

Re: Capturar eventos de foco globales

Mensaje por ms999 »

A ese codigo le falta bastante para ser de AutoIt... xD solo un poco
Empezando que no entiendo bien que es lo que queres. Escribir el nombre de la ventana activa cuando presionas CTRL + ALT + V y que si el nombre de la ventana es el mismo que no lo escriba?
bueno suponiendo que es eso lo que queres empezemos con esto

No es necesario poner HotKeySet() dentro de un bucle porque no es necesario Declarar esa funcion tantas veces, solo haciendolo una vez ya esta bien

El bucle no se escribe asi While(1) como posiste, sino que se pone While 1 o While true o While 2 = 2 o la condicion que quieras.

No se que intentaste hacer con Swithc pero no funciona de esa manera y tampoco lo veo tan necesario para este caso, mira como ejemplo de Switch los codigos que te tira el KodaForm.

Las Variables que queres que sobrevivan cuando termine la funcion las debes guardar en una variable Global, ya que todas las variables que creas dentro de una funcion sin especificar si es Local, Global, son por defecto tomadas como locales, por lo tanto dejan de existir una vez que terminó la función.

La convinación de teclas que elegiste no me parece la mejor, ya que se confunde con la acción PASTE de windows.. (esto es a elección, yo eleji SHIFT + F2)

Y por ultimo agregué una función y una hotkey para terminar el script de manera sencilla.

Código: Seleccionar todo

HotKeySet("{ESC}", "_exit")
HotKeySet("+{F2}", "TituloVentana")

Global $TituloAnterior
While True
	Sleep(100)
WEnd

Func TituloVentana()
	Local $TituloVentana = WinGetTitle("[active]")
	If $TituloVentana <> $TituloAnterior Then
		$TituloAnterior = $TituloVentana
		ConsoleWrite(@CRLF & $TituloVentana & @CRLF)
	EndIf
EndFunc   ;==>TituloVentana
Func _exit()
	Exit
EndFunc
Espero que esto te sirva y pregunta cuanto quieras! disculpa si aveces :smt021 mucho!
Avatar de Usuario
melvinhn
Hacker del Foro
Mensajes: 75
Registrado: 16 Jul 2011, 22:14
Ubicación: Honduras, Cortes, Puerto Cortes, Col Miraflores
Contactar:

Re: Capturar eventos de foco globales

Mensaje por melvinhn »

Por eso les pregunto a los experto, tu sabes uno que apenas empieza
tiene que ir aprendiento cada dia.

justo lo que queria me sirvio de mucho el codigo
Responder