WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

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

WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Ximorro »

Tengo un GUI en el que quiero controlar cuándo pierdo y recupero el foco. Uso la captura de los mensajes WM_SETFOCUS y WM_KILLFOCUS. Pero me estoy volviendo mico que esto no va y más simple no puede ser. :smt013

He ido quitando cosas del programa por si estaba pegándose con algo (por ejemplo también capturaba otros mensajes, había hotkeys globales y de formulario...). Pero el resultado es tan simple que no comprendo por qué no funciona. :smt010

El caso es que sólo funciona... ¡si el GUI no tiene controles!. Eso lo hace bastante inútil, claro. :smt003

Os pongo el código reducido que sigue sin funcionar, fijaos que ni una etiqueta se puede poner. Si comentáis la creación de la etiqueta funciona perfectamente.
Está comentada la creación y uso de un botón, por poner un control que toma foco, pero como digo con una simple etiqueta también falla.
Lo que hace es perder el foco nada más ejecutarse ¡aunque en realidad lo sigue teniendo! y a partir de ahí ni se entera de que lo está perdiendo o ganando.
La única manera es cuando minimizo, al restaurar sí toma foco e instantáneamente me manda un KILLFOCUS, aunque no lo ha perdido...

Código: Seleccionar todo

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

$focoGUI = GUICreate("Ventana Foco", 200, 100, 10, 10)
GUICtrlCreateLabel("Soy una etiqueta.", 16, 13, 100, 15) ; <-- Si se comenta esta línea todo va como la seda...
;$btnCerrar = GUICtrlCreateButton("Cerrar", 50, 50, 97, 25)

GUIRegisterMsg($WM_SETFOCUS, "WM_SETFOCUS")
GUIRegisterMsg($WM_KILLFOCUS, "WM_KILLFOCUS")
GUISetState(@SW_SHOW)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE ;, $btnCerrar
			Exit
	EndSwitch
WEnd

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

Func WM_KILLFOCUS($hWnd, $iMsg, $iwParam, $ilParam)
	ConsoleWrite("Foco perdido: " & WinGetTitle($hWnd) & @LF)
	Return $GUI_RUNDEFMSG
EndFunc
Saludos...
Última edición por Ximorro el 25 Mar 2010, 10:29, editado 1 vez en total.
"¿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: WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Chefito »

Juer macho, que raro que pase eso :smt017 .
Bueno, como paso de calentarme el coco para ver que pasa con eso, he visto que te puede servir la captura de otro mensaje de la ventana: $WM_ACTIVATEAPP.
Detecta cuando activas la aplicación (consigue el foco) y cuando se desactiva (pierde el foco). Te dejo el código cambiado.

Código: Seleccionar todo

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>

$focoGUI = GUICreate("Ventana Foco", 200, 100, 10, 10)
GUICtrlCreateLabel("Soy una etiqueta.", 16, 13, 100, 15) ; <-- Si se comenta esta línea todo va como la seda...
$btnCerrar = GUICtrlCreateButton("Cerrar", 50, 50, 97, 25)

GUIRegisterMsg($WM_ACTIVATEAPP, "WM_ACTIVATEAPP")
;~ GUIRegisterMsg($WM_KILLFOCUS, "WM_KILLFOCUS")
GUISetState(@SW_SHOW)

While 1
   Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE ;, $btnCerrar
         Exit
   EndSwitch
WEnd

Func WM_ACTIVATEAPP($hWnd, $iMsg, $iwParam, $ilParam)
   If $iwParam Then		;si $iwParam=1 entonces ha ganado el foco (la aplicación está activa)
	   ConsoleWrite("Foco adquirido: " & WinGetTitle($hWnd) & @LF)
   Else		;si $iwParam=0 entonces ha perdido el foco (la aplicación no está activa)
		ConsoleWrite("Foco perdido: " & WinGetTitle($hWnd) & @LF)
	EndIf
   Return $GUI_RUNDEFMSG
EndFunc
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: WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Ximorro »

¡Gracias! Lo había conseguido con el mensaje WM_ACTIVATE, ahora iba a poner la solución, pero mejor no porque la tuya es más simple ¡no llegué a ver WM_ACTIVATEAPP!

El problema con WM_ACTIVATE es que da por separado activación y minimización, y a veces me da minimizado pero activado (en ese caso hay que ignorar la activación, manda el minimizado). Además mi caso era un poco más complicado, porque lo que quiero es que una ventana se minimice sola a la bandeja cuando esté desactivada cierto tiempo, si tengo:

Case $GUI_EVENT_MINIMIZE, $btnMinimizar
_MinimizaABandeja()


Resulta que al presionar el en botón de minimizar de la barra de título, WM_ACTIVATE sí me dice que está minimizado e inactivo, pero si llega ahí por presionar el botón $btnMinimizar, realmente no hay un evento MINIMIZE, y WM_ACTIVATE no es suficiente, entonces tengo que ver además si la ventana está oculta, que no es lo mismo que minimizada... y el caso es que si llega por $GUI_EVENT_MINIMIZE el mensaje WM_ACTIVATE me llega antes de estar oculta, en ese caso lo que hay que mirar es si está minimizada.

Encima cuando el timer finaliza y se autominimiza (oculta) automáticamente, aunque lógicamente la ventana se desactiva ¡no me notifica el WM_ACTIVATE!, así que me ha tocado volver a comprobarlo cuando lo lanzo por código sin estar respondiendo a un evento del GUI.

¡Menudo follón! Bueno pero ya va...

Qué mala suerte que no llegué a ver WM_ACTIVATEAPP, total sólo me faltaban 3 letritas ¡gracias!
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Ximorro »

Ojo, había un fallo en los Return, tienen un ";" que no toca:
Return; $GUI_RUNDEFMSG

Ya lo he cambiado en mi post pero el código de Chefito lo tiene por el copy-paste (mea culpa). <== Arreglados ambos códigos (¿no tenemos el estilo de fuente tachada?)
Originariamente no estaba, fue una de las muchas pruebas que hice pero se me pasó volver a quitarlo. Si usáis estos códigos que no esté ese ";", lo correcto es mandar un $GUI_RUNDEFMSG para que Windows siga procesando el mensaje.

Por cierto, supongo que SETFOCUS y compañía no va porque lo que pasa es que ese mensaje se da también para los controles, que al fin y al cabo para Windows también son ventanas (aunque no aplicaciones). Me pasa que toma el foco la ventana principal, pero enseguida se lo da a un control, con lo que la ventana principal lo pierde.
No sería problema si AutoIt notificara los SET/KILL-FOCUS de los controles de nuestro GUI (¡en realidad sería ideal!), pero no lo hace, con lo que no sabes si el foco se ha ido a un control tuyo (con lo que tu ventana SI tiene foco) o a un control de otra ventana (con lo que tu ventana ya no tiene foco).

Pues eso, este caso concreto se soluciona con WM_ACTIVATEAPP (o con WM_ACTIVATE si quieres controlar minimizado o de qué manera se ha activado, pues este mensaje da más datos), pero me parece que SET/KILL-FOCUS no están tratados correctamente en AutoIt...

Otra diferencia entre WM_ACTIVATEAPP y WM_ACTIVATE, el primero me notifica una pérdida de foco si hago un GUISetState(@SW_HIDE), ¡pero el segundo no notifica la desactivación!
Última edición por Ximorro el 26 Mar 2010, 09:33, editado 1 vez en total.
"¿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: WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Chefito »

Ximorro escribió:Ojo, había un fallo en los Return, tienen un ";" que no toca:
Return; $GUI_RUNDEFMSG

Ya lo he cambiado en mi post pero el código de Chefito lo tiene por el copy-paste (mea culpa).
Corregido. Gracias, se me pasó :smt002 .
Ximorro escribió:Por cierto, supongo que SETFOCUS y compañía no va porque lo que pasa es que ese mensaje se da también para los controles, que al fin y al cabo para Windows también son ventanas (aunque no aplicaciones). Me pasa que toma el foco la ventana principal, pero enseguida se lo da a un control, con lo que la ventana principal lo pierde.
Estoy totalmente de acuerdo. Yo también pensé que pasaba algo así. No le veo otra cosa :smt002 .

Respecto a los mensajes que recibes con WM_ACTIVATE también los ví. Lo descarté a causa del mensaje que mandaba de más al minimizar. Vi que WM_ACTIVATEAPP mandaba solo los mensajes que necesitabas para perdida y ganancia del foco y por eso la posteé.
Una solución para ver el estado de tu ventana es meter en la función WM_ACTIVATEAPP un guigetmsg() y comparar sus resultados. Por ejemplo compararlo con las variables $GUI_EVENT_MINIMIZE, $GUI_EVENT_RESTORE, $GUI_EVENT_MAXIMIZE para saber si se ha minimizado, restaurado o maximizado. O comparar el resultado con alguna otra de las variables.

Otra solución es capturar los mensajes de WM_SIZE. Mira el msdn: http://msdn.microsoft.com/en-us/library ... S.85).aspx
Verás los resultados de los mensajes:
SIZE_MAXHIDE = 4 SIZE_MAXIMIZED = 2 SIZE_MAXSHOW = 3 SIZE_MINIMIZED = 1 SIZE_RESTORED = 0
O sea, da 0 cuando restauras la ventana (y parece ser que cuando le cambias el tamaño), 1 cuando minimizas, 2 cuando maximizas.

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: WM_SETFOCUS y WM_KILLFOCUS no capturables con controles

Mensaje por Ximorro »

Pues es que resulta que sí tengo que ver si está minimizada, no sólo si pierde foco, porque el timer lo tengo que activar cuando pierde foco pero está visible. El timer es precisamente para autominimizar la ventana cuando lleva cierto tiempo desactivada, así que si se minimiza no tengo que ponerlo (y en realidad desactivarlo si está puesto).
Así que WM_ACTIVATE me servía, pero también tenía que ver si estaba oculta por si llegaba ahí por otro lado. Eso lo miro precisamente con BitAnd(WinGetState($GUI_Enc), 2).
Efectivamente podría haber usado WM_ACTIVATEAPP y usar el WinGetState para ver también si estaba minimizada.

Pero veo un poco lío usar un WinGetMsg dentro de la función del mensaje, tengo un GUI controlando sus eventos en el bucle principal y si aquí capturamos algunos puedo perderlos, tendría que repetir toda la lógica. Además un $GUI_EVENT_MINIMIZE se da antes que el mensaje, con lo que el evento realmente se capturaría en el WinGetMsg del bucle principal, y después me mandaría a la función de la notificación, con lo que otro WinGetMsg no vería ese minimizado, que ya ha sido consumido...

Había usado WM_SIZE en otro programa para hacer cosas mientras el usuario está redimensionando la ventana, pero en ese caso sólo necesitaba lParam, que te dice el tamaño. Muy útil tener los valores numéricos de wParam, ya que AutoIt no los tiene y la MSDN no te pone directamente qué valen esas constantes, hay que estar buscándolas... ¡me las apunto!
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Responder