Problemilla al Capturar tecla con hooks

Tus preguntas. Algoritmos o Grupos de Comandos formando Programas Escripts.
Responder
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Quiero capturar el teclado con las apis hooks.

Para ello, como que no controlo mucho estas apis, me he basado en la función _KeyProc() de Chefito de este post:
http://www.emesn.com/autoitforum/viewto ... on+winlist

ya que el primer código que hizo para esto mismo en un post más antiguo no lo encuentro...

Resulta, que todo funciona bien, salvo que es como si recibiera dos veces seguida la misma tecla, por lo que si compruebo con un select/switch si por ejemplo sse ha pulsado la tecla enter y muestro un mensaje con Msgbox(), este se muestra dos veces cada vez que pulso la tecla intro.

el código que he hecho es este:

Código: Seleccionar todo

HotKeySet("{esc}", "ExitFile")

Global $KeyProcRegister, $HMod, $HHook, $Kernel32DLL, $User32DLL

DllStart()
KeyboardHooksStart()

Func KeyboardHooksStart()
 $KeyProcRegister=DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
  If $KeyProcRegister=0 Then Return 0
 $HMod=DllCall($Kernel32DLL, "hwnd", "GetModuleHandle", "ptr", 0)
  If (Not IsArray($HMod) Or @Error<>0) Then Return 0
 $HHook=DllCall($User32DLL, "hwnd", "SetWindowsHookEx", "int", $WH_KEYBOARD_LL, "ptr", DllCallbackGetPtr($KeyProcRegister), "hwnd", $HMod[0], "dword", 0)
  If (Not IsArray($HHook) Or @Error<>0) Then Return 0
 Return 1
EndFunc

Func _KeyProc($NCode, $WParam, $LParam)
 Local $KeyHookStruct, $VKeyCode
  $KeyHookStruct=DllStructCreate("dword;dword;dword;dword;ptr", $LParam)
   If ($KeyHookStruct=0 And @Error<>0) Then Return _WinAPI_CallNextHookEx($HHook, $NCode, $WParam, $LParam)
  $VKeyCode=DllStructGetData($KeyHookStruct, 1)
   If ($VKeyCode=0 And @Error<>0) Then Return _WinAPI_CallNextHookEx($HHook, $NCode, $WParam, $LParam)
  Select
   Case $VKeyCode=13 ;Si se pulsa la tecla "enter"
    Msgbox(0, "", "¡Has pulsado la tecla ENTER!")
  EndSelect
  Return _WinAPI_CallNextHookEx($HHook, $NCode, $WParam, $LParam)
EndFunc

Func DllStart()
 $User32DLL=DllOpen("User32.dll")
  If $User32DLL=-1 Then Return 0
 $Kernel32DLL=DllOpen("Kernel32.dll")
  If $Kernel32DLL=-1 Then Return 0
 Return 1
EndFunc

Func DllQuit()
 $User32DLL=DllClose($User32DLL)
 $Kernel32DLL=DllClose($Kernel32DLL)
 Return ""
EndFunc

Func ExitFile()
 $HHook=DllCall($User32DLL, "int", "UnhookWindowsHookEx", "hwnd", $HHook[0])
 DllCallbackFree($KeyProcRegister)
 DllQuit()
 Exit
EndFunc

While 1
 Sleep(10)
Wend
¿Porqué puede ser?

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

Re: Problemilla al Capturar tecla con hooks

Mensaje por Chefito »

Mmmmm....no tengo el código aquí, pero así de pronto creo recordar que tenías que filtrar el mensaje que te devolvía el parámetro wparam.
Si pulsabas una tecla creo que te devolvía el valor 256, y si soltabas 257.

Pon una condición que envuelva todo el código de la función con el valor que te interese y solo se te ejecutara una vez el código, o al pulsar o a soltar.

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 ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Pues así probando he observado eso, porque me sonaba que había algo así, de cuando tratamos este tema hace ya un tiempo, pero no estaba muy seguro si eso sería así, pues $WParam da el estado también de la pulsación de ALT (pulsado ) y supongo que liberado.

Si se pulsa ALT, $WParam vale 260 ¿como se comprobaría entonces cuando está liberada?.

Esque, el post donde lo explicabas con un código más ámplio que tocaba los hooks (oftopic captura de teclado) o algo así, ya no aparece en el foro.
Probaré con lo que dices de la condición y el 256 y 257 de $WParam.

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Problemilla al Capturar tecla con hooks

Mensaje por Ximorro »

"Si se pulsa ALT, $WParam vale 260"
pues entonces cuando no valga 260 es cuando está liberada ¿no? :smt002

Una cosa, haces una cosa un poco rara con la organización del código. Creo que no es buena idea mezclar el código principal con definiciones de funciones. Interrumpes el código principal en:
DllStart()
KeyboardHooksStart()


Después pones las funciones, y al final continúas el código principal:
While 1
Sleep(10)
Wend


Es mejor poner ese bucle después del KeyboardHooksStart() y cuando acaba el código principal poner todas las funciones, hace más fácil el seguimiento.

Respecto al hook, simplemente puedes ir sacando por consola los valores wparam y lparam en cada llamada, así ves lo que valen cuando pulsas las teclas que te interesan. Con esos valores luego haces las comprobaciones pues con las mismas teclas dará los mismos valores.
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Cierto lo que dices del código.

Ya tengo todo el teclado capturado.
Ahora, como haría que la tecla pulsada no llegara a la aplicación activa?

Por ejemplo:

Si la ventana del notepad está activa, y el programa que captura las teclas pulsadas está ejecutándose en segundo plano.
Al pulsar la "a", en el notepad se escribiría una "a".
¿Como capturaría con la función _KeyProc() la "a" pero haciendo que no se llegara a escribir la "a" en el notepad? (como si no hubiera recibido la pulsación).

Creo que retornando un valor vacío, pero no estoy muy seguro...

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Problemilla al Capturar tecla con hooks

Mensaje por Ximorro »

No lo he probado pero probablemente impidiendo que siga la cadena de hooks es una manera (es decir, no llamar a _WinAPI_CallNextHookEx después de capturar la tecla).
Devolviendo por ejemplo "0", como dices, u otro valor.
Pero eso no tengo claro que sea del todo seguro, pues tu gancho probablemente ha sido llamado desde otro y habrá que devolver un valor adecuado (igual si devuelves 0 estás diciendo que ha habido un error, y no es verdad). Lo mejor es sí llamar a _WinAPI_CallNextHookEx, pero con los parámetros modificados con los valores como si no se hubiera pulsado esa tecla, así la cadena se procesa correctamente, pero los siguientes no ven la tecla...

Una cosa importante, en estas funciones de ganchos, así como las que procesan mensajes (como las que se registran con GUIRegisterMsg) es importantísimo salir de la función lo antes posible, y de hecho la ayuda oficial desaconseja efusivamente no usar funciones bloqueantes tipo MsgBox. Ya sé que lo estás poniendo para depurar, pero te aconsejo cambiarlo por un ConsoleWrite y usar la consola para ver esos mensajes de ayuda (con Scite no te hace falta ejecutarlo desde una consola, Scite ya tiene su propia consola)
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Miraré la ayuda de la función _WinAPI_CallNextHookEx entonces, porque sinceramente, no se bien que hace :P.
Aunque, sí he podido comprobar, que si no se hace ese return, el teclado se queda como colgao, hasta que se consigue cerrar el proceso.

Es costumbre de depurar con Msgbox(), y cierto, cada vez me gusta menos, justo por lo que dices: En según qué da problemas. En esto de capturar el teclado mismamente, me hace una cosa rarísima.

Si pongo un Msgbox() para que salte al pulsar por ejemplo, CTRL izquierdo, y después de este MsgBox() pongo un Exit, al aceptar la ventanita que aparece al pulsar CTRL izquierdo (suponiendo que el programa tenga interfaz), esta se cierra, pero el teclado no responde bien. Y es que, el proceso del autoit3 sigue activo... y hay que matarlo desde el administrador de tareas.

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

Como anular teclas especiales

Mensaje por Chefito »

Pues yo en casi todos los códigos que hice de tratamiento de teclas por hook no llamé nunca al proximo hook.....y nunca me ha pasado nada raro :smt003 . Que es una mala práctica? Supongo que sí :smt005 .
Seguro que Win tiene que gestionar eso a parte, porque si no supongo que pasaría alguna cosa rara....digo yo.

Para hacer que la tecla no llege a su destino, la práctica que se suele hacer es retornar -1.

Pongo otro de mis ejemplos que creo que ha desaparecido del foro. Se trata de anular teclas especiales como la de los dos controles, tabulador, tecla win, menu contextual, etc. Para anular alguna tecla en especial, pues poneis su código y return -1 :smt023 .
Con esto se puede anular desde una tecla, hasta un rango de teclas, e incluso todo el teclado.

Código: Seleccionar todo

#include <Misc.au3>
#include <GUIConstantsEx.au3>
                           
OnAutoItExitRegister("OnAutoItExit")	;si te da error esta línea, es porque tienes una versión anterior de AutoIt. Coméntala y el script funcionará sin problemas.
Global $codigo=0
Global $dll = DllOpen("user32.dll")
Global Const $WH_KEYBOARD_LL = 13
Global $hStub_KeyProc = DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
Global $hmod = DllCall("kernel32.dll", "hwnd", "GetModuleHandle", "ptr", 0)
Global $hHook = DllCall("user32.dll", "hwnd", "SetWindowsHookEx", "int", _
      $WH_KEYBOARD_LL, "ptr", DllCallbackGetPtr($hStub_KeyProc), "hwnd", $hmod[0], "dword", 0)
Global $gui=GUICreate("My GUI Button")
GUISetState()
While 1
   Sleep(10)
        $msg = GUIGetMsg()
        Select
            Case $msg = $GUI_EVENT_CLOSE
                ExitLoop            
        EndSelect
WEnd

Func _KeyProc($nCode, $wParam, $lParam)
    Local $ret, $KEYHOOKSTRUCT, $posmouse
      If WinActive($gui) Then   ;condición de si está la ventana activa del programa ejecutar la anulación de teclas.

         If $wParam = 256 Then   ;cuando pulsas, o mantienes pulsada una tecla, entra en este bloque.
            $KEYHOOKSTRUCT = DllStructCreate("dword;dword;dword;dword;ptr", $lParam)
            $codigo=DllStructGetData($KEYHOOKSTRUCT, 1)
            ;ConsoleWrite($codigo & @CRLF)   ;<<<<<=== aquí te dice el código de tecla que pulsas. Esto solamente es informativo. Quitalo.
            ConsoleWrite("El codigo de la tecla que has pulsado es " & $codigo & "  puede que sea la tecla " & chr($codigo) &@CRLF)   ;<<<<<=== aquí te dice el código de tecla que pulsas. Esto solamente es informativo. Quitalo.

            Switch $codigo
               Case 162   ;pulsas tecla control izquierda
                  ConsoleWrite("has pulsado y anulado ctrl izquierdo" & @CRLF)
                  Return -1
               Case 163   ;pulsas tecla control derecha
                  ConsoleWrite("has pulsado y anulado ctrl derecho" & @CRLF)
                  Return -1
               Case 91
                  ConsoleWrite("has pulsado y anulado la tecla win" & @CRLF)
                  Return -1
               Case 93
                  ConsoleWrite("has pulsado y anulado la tecla menu contextual" & @CRLF)
                  Return -1
               Case 160
                  ConsoleWrite("has pulsado y anulado la tecla shift izquierda" & @CRLF)
                  Return -1
               Case 161
                  ConsoleWrite("has pulsado y anulado la tecla shift derecha" & @CRLF)
                  Return -1
               Case 9
                  ConsoleWrite("has pulsado y anulado la tecla tab" & @CRLF)
                  Return -1
               Case 20
                  ConsoleWrite("has pulsado y anulado la tecla bloquear mayusculas" & @CRLF)
                  Return -1
               Case 123
                  ConsoleWrite("has pulsado y anulado la tecla f12" & @CRLF)
                  Return -1
            EndSwitch
         ;ElseIf $wParam=257 Then
            ;$wparam es igual a 257 cuando sueltas la tecla pulsada. En este ejemplo no hace falta :)=
         EndIf   
         if $wparam=260 Then 
            ConsoleWrite("has pulsado y anulado la tecla Alt" & @CRLF)
            Return -1
         EndIf
         
      EndIf   ;final de la condición que dice "si está activa la ventana del programa, ejecuta la anulación de teclas."

EndFunc  ;==>_KeyProc

Func OnAutoItExit()
   DllClose($dll)
    DllCall("user32.dll", "int", "UnhookWindowsHookEx", "hwnd", $hHook[0])
    DllCallbackFree($hStub_KeyProc)
EndFunc  ;==>OnAutoItExitm
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 ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

¡Gracias por postearlo nuevamente!

Este era el código que buscaba, y decía que no encontraba...

Yo sabía que había que retornar algo, pero no el qué.
Cierto ¡-1! :).

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Problemilla al Capturar tecla con hooks

Mensaje por Ximorro »

Jonny no es buena idea poner MsgBox dentro de gestión de ganchos o mensajes... ¡pero menos un EXIT! Sal de la función correctamente, y si quieres abortarla sal con un valor de error y fuera de ella lo compruebas, si ha habido error entonces puedes cerrar el programa correctamente (llamando a UnhookWindowsHookEx y esas cosas que se hacen al final).

Chefito veo que tu programa además de anular esas teclas cambia la "A" por "C", así de regalo ;-) (si es eso lo que hace "case 65 -> return 68")

Qué puñetero el ALT, que va a parte ¿verdad?, no puede ser tan especial como el CTRL, SHIFT o WIN, no, ¡tiene que ser más especial todavía! :smt003

Igual por defecto Windows sigue la cadena de ganchos si no se hace Return (como parece decir la ayuda de GUIRegisterMsg, aunque es un poco confuso). AutoIt en la ayuda de GUIRegisterMsg dice que por defecto a la salida de la función se continúa con el "manejador de mensajes". A menos que devuelvas algo diferente de $GUI_RUNDEFMSG, en ese caso se para la reemisión de mensajes y se toma como consumido.

A lo mejor con los ganchos es parecido, pero en este caso al devolver -1 estaríamos parando la cadena. Puede que no se note porque si Windows tiene ganchos al teclado los habrá puesto antes que nuestro programa, con lo que es él el que llama al nuestro, que al ser el último que hemos puesto no afecta a los siguientes porque no hay. El problema sería si se estableciera otro gancho después del nuestro, entonces quizás no funcionara.

Si eso es así es mejor hacerlo de forma que sí siga la cadena, puede no importarnos porque "no nos afecta", pero si fuera al revés, que el gancho anterior al nuestro no pasa la notificación... ¡entonces nuestro gancho no funcionaría aunque estuviera bien programado!, ¿a qué eso fastidiaría bastante? Entonces nos cxgxríamos en el "programador chapucero que no ha hecho bien su gancho y ahora es imposible hacer que el nuestro funcione" :smt005 Mejor que no seamos nosotros ese programador, je, je. No digo que el Return -1 pare la cadena, no tengo ni idea. Lo que digo es que habría que comprobarlo.

Con AutoIt se podría probar ejecutando dos programas diferentes que pongan ganchos, y que ninguno llame a CallNextHookEx a ver si alguno no funciona. Mejor que devuelvan algo, como el -1, 0, etc., a ver si resulta que el -1 es un valor especial y no para la cadena, y por ejemplo cero o valores positivos (o no poner Return) sí.

Hala Jonny, haz esa prueba y nos dices. Será un conocimiento interesante.
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

De momento he probado a devolver Return -1, como dice Chefito, y efectivamente el script procesa lo que pongamos en la condición de si X tecla está pulsada y la anula, de manera que no es procesada por ninguna otra aplicación, incluso aunque intente tratar la tecla con hooks.

Lo otro que comentas; Imagino que te he entendido...

Viene a responder a lo que acabo de decir:
Si ejecutáramos dos veces el mismo programa, que capturara por medio de hooks las mismas teclas, el último en ejecutarse y por tanto en capturar las teclas es el que primero recibe su pulsación, y de él depende que el primer programa ejecutado pueda llegar a capturar la pulsación de las teclas o no, dependiendo del retorno de este último que se ha ejecutado.

Imagino, que esto no podrá ebitarse de ninguna manera (estaría muy bien que se pudiera).

De todas formas, esto es demasiado teórico, así que me pierdo un poco... :P

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

Re: Problemilla al Capturar tecla con hooks

Mensaje por Chefito »

Ximorro escribió:Jonny no es buena idea poner MsgBox dentro de gestión de ganchos o mensajes... ¡pero menos un EXIT! Sal de la función correctamente, y si quieres abortarla sal con un valor de error y fuera de ella lo compruebas, si ha habido error entonces puedes cerrar el programa correctamente (llamando a UnhookWindowsHookEx y esas cosas que se hacen al final).
Totalmente cierto. No se debe salir por las buenas sin liberar el hook y la llamada de función del teclado, y sin cerrar la dll. Puede que te haga cosas raras que no debe hacerte.
Si te das cuenta, en el código que he puesto, esto está dentro de la función OnAutoItExit, que se utilizaba en versiones anteriores de autoit, para ejecutarse justo antes de salir del programa y despreocuparte de que se tenga que ejecutar siempre que se salga. No recordé que esto había cambiado y ahora hay que utilizar la función OnAutoItExitRegister para registrar esta función que se ejecuta antes de salir del programa. Lo he editado poniendo un OnAutoItExitRegister("OnAutoItExit") para que funcione con la nueva versión de AutoIt.
También puedes llamar a la función que hace esto justo antes de tu exit, pero veo más cómodo lo anterior.
Ximorro escribió:Chefito veo que tu programa además de anular esas teclas cambia la "A" por "C", así de regalo (si es eso lo que hace "case 65 -> return 68")

Jejejeje, pues no. Sería algún pedazo de código que se coló en la época que lo hice :smt003 . Ya lo he quitado :smt002 .
Hice y expliqué otra forma para cambiar una tecla por otra en otro código, pero seguro que también a desaparecido del foro :smt010 .
Ximorro escribió:Qué puñetero el ALT, que va a parte ¿verdad?, no puede ser tan especial como el CTRL, SHIFT o WIN, no, ¡tiene que ser más especial todavía!
Jejejeje, sí, siempre tiene que haber un caso más especial y más engorroso que los demás :smt005 .

Y respecto a lo de que se pare la cadena de ganchos, que puede ser que win por su cuenta la continue sin problemas aunque tu jueges con ella, que afecte a otros programas que utilicen técnicas parecidas, y que el valor -1 que devolvemos sea un caso particular para poder hacer esto sin problemas, pues no se, habría que investigarlo. Solo digo que creo que no soy el único que lo hacía así. Creo recordar que había más gente :smt017 .

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 ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Estoy intentando capturar la pulsación de varias teclas a la vez, con hooks (La función _KeyProc() de Chefito), pero no me funciona.

En el select, pongo algo como:

Código: Seleccionar todo

Case (($codigo=45 And $WParam=256) And ($codigo=123 And $WParam=256)) ;Si se pulsan las teclas "insert" y "F12".
pero no funciona. Por lo visto, el callback solo recibe una tecla por cada llamada en $LParam.

Con la tecla "alt" y otra tecla (a la vez) sí que funciona, porque se reciben en $WParam y $LParam respectivamente, pero ¿Hay alguna forma de detectar más de una tecla pulsada de esta manera?. Ya se que con _IsPressed, o la api GetKeyState puedo hacerlo, pero ya que tengo hecho el código así, si puedo hacer la comprobación añadiendo un case al select del callback, mejor...

Acias,

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Problemilla al Capturar tecla con hooks

Mensaje por Ximorro »

Me parece que no hay manera, esto va dando las teclas una a una, habrá que hacerlo con KeyPressed.
De todas maneras tu condición es incorrecta, no se cumplirá nunca, ahí estás diciendo que $WParam debe ser 256 y $codigo debe ser A LA VEZ 45 y 123.
El AND central debería ser un OR, pero como digo aún así sólo capturarás ALT+algo, otras combinaciones creo que no se pueden capturar con esto.

Pues igual lo de el ALT por separado es un parche para poder capturar esa combinación desde el propio gancho, aunque en principio no estaría pensado para eso. Puestos a parchear ya podían haber hecho especiales CTRL, SHIFT, pero en fin, es lo que hay.
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Tienes razón, pero quería expresarlo así. Al valer 256 $WParam si la tecla está pulsada, pensaba que podría recibir los dos códigos a la vez si se pulsaban dos teclas, por eso, quería comprobar si $Codigo valía los dos valores, y $WParam valía 256. Pero sí, podía afinarse más esa expresión, porque ahora que lo pienso: comprobando solo una vez $WParam=256, debería ser lo mismo.

¡Vaya fastidio! que no funcione.
Lo curioso, es, que si en el mismo case que compruebo si se ha pulsado una tecla, compruebo con GetKeyState (_IsPressed() no lo he probado) si otra está pulsada, tampoco me funciona...
Solo, si dentro del case pongo una condición para ver con GetKeyState, si otra tecla está pulsada.

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

Re: Problemilla al Capturar tecla con hooks

Mensaje por Chefito »

Haber haber.......Jonny, tu mismo has dicho la solución en el último post a tu problema. Otra cosa es que no se te ocurra como aplicarlo :smt003 .

Decir que el comando _IsPressed es muy bueno para hacer estas cosas. También se puede hacer por apis, pero puede que no interese complicarse la vida. Según lo que quieras, las necesidades y los gustos.
Por supuesto, no tengo que decir que si quereis anular las teclas, cambiarlas, o hacer cosas raras con éstas, es casi inevitable utilizar apis.

Se pueden detectar con el código anterior sin problemas dos o más teclas pulsadas al mismo tiempo con las condiciones adecuadas. Cuantas más teclas juntas quieras dectectar más condiciones tendrás que poner :smt024 .
Con este código también se podría diferenciar la pulsación de teclas según su orden. En el ejemplo que te pongo a continuación he ignorado esto último.

He creado el código siguiendo tu ejemplo, que detecte la pulsación de las teclas insertar y f12 al mismo tiempo.

Código: Seleccionar todo

#include <Misc.au3>
#include <GUIConstantsEx.au3>
                           
OnAutoItExitRegister("OnAutoItExit")   ;si te da error esta línea, es porque tienes una versión anterior de AutoIt. Coméntala y el script funcionará sin problemas.
Global $codigo=0
Global $dll = DllOpen("user32.dll")
Global Const $WH_KEYBOARD_LL = 13
Global $hStub_KeyProc = DllCallbackRegister("_KeyProc", "long", "int;wparam;lparam")
Global $hmod = DllCall("kernel32.dll", "hwnd", "GetModuleHandle", "ptr", 0)
Global $hHook = DllCall("user32.dll", "hwnd", "SetWindowsHookEx", "int", _
      $WH_KEYBOARD_LL, "ptr", DllCallbackGetPtr($hStub_KeyProc), "hwnd", $hmod[0], "dword", 0)
Global $gui=GUICreate("My GUI Button")
GUISetState()
While 1
   Sleep(10)
        $msg = GUIGetMsg()
        Select
            Case $msg = $GUI_EVENT_CLOSE
                ExitLoop            
        EndSelect
WEnd

Func _KeyProc($nCode, $wParam, $lParam)
    Local $ret, $KEYHOOKSTRUCT, $posmouse
      If WinActive($gui) Then   ;condición de si está la ventana activa del programa ejecutar la anulación de teclas.

         If $wParam = 256 Then   ;cuando pulsas, o mantienes pulsada una tecla, entra en este bloque.
            $KEYHOOKSTRUCT = DllStructCreate("dword;dword;dword;dword;ptr", $lParam)
            $codigo=DllStructGetData($KEYHOOKSTRUCT, 1)
            ;ConsoleWrite($codigo & @CRLF)   ;<<<<<=== aquí te dice el código de tecla que pulsas. Esto solamente es informativo. Quitalo.
;~             ConsoleWrite("El codigo de la tecla que has pulsado es " & $codigo & "  puede que sea la tecla " & chr($codigo) &@CRLF)   ;<<<<<=== aquí te dice el código de tecla que pulsas. Esto solamente es informativo. Quitalo.

            Select
               Case ($codigo=45 And PulsadaKey(123)) or ($codigo=123 And PulsadaKey(45))
                  ConsoleWrite("has pulsado insertar y f12 al mismo tiempo" & @CRLF)
;~                   Return -1
			EndSelect
		  EndIf
      EndIf   ;final de la condición que dice "si está activa la ventana del programa, ejecuta la anulación de teclas."

EndFunc  ;==>_KeyProc

Func OnAutoItExit()
   DllClose($dll)
    DllCall("user32.dll", "int", "UnhookWindowsHookEx", "hwnd", $hHook[0])
    DllCallbackFree($hStub_KeyProc)
EndFunc  ;==>OnAutoItExitm


Func PulsadaKey($codigoKey)
    $ret = DllCall("user32.dll", "long", "GetKeyState", "long", $codigoKey)
    If $ret[0]=0 Or $ret[0]=1 Then
        $pulsada=False
    Else
        $pulsada=True
    EndIf
    Return $pulsada
EndFunc
Recuerda que en el código hay una condición que limita la detección de teclas a la ventana activa del script. Si quieres que sea en todo windows tienes que quitarla.

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 ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Ajá....

Yo me dejaba la segunda condición que pones.

Solo comprobaba la pulsación de una manera concreta:

Código: Seleccionar todo

case $Codigo=45 and PulsadaKey(123)==1
Aunque algo más tenía que hacer mal, porque tampoco funcionaba...

A ver así que tal.

Salu2!
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Problemilla al Capturar tecla con hooks

Mensaje por Jonny »

Hola

Al fin, me funciona la captura de una tecla y de varias simultáneamente, con el código de Chefito.

Por cierto ¡menudo susto!... No me funcionaba al principio (como me pasaba antes de postear el problema), hasta que me di cuenta, que era porque capturaba la tecla insert individualmente, y después Insert+F12... Haciéndolo al revés, funciona perfecto.

Pero he observado algo, que no termina de convencerme y no se si podrá solucionarse...

Hay teclas, como "Enter" o "Tabulador", que con esta forma de capturarlas, se ejecutan únicamente cuando la tecla está pulsada. En cuanto se libera, deja de ejecutarse el código asociado al case que corresponda a su código virtual...

Pero hay otras en cambio, que no. Si pulsamos y mantenemos pulsada la tecla "Insert" por ejemplo dos segundos, al soltarla, se seguirá ejecutando el código que le hayamos puesto, hasta que se ejecute tantas veces como Windows considera que se ha pulsado la tecla. Es, como si al mantenerla dos segundos, equivaliera a pulsarla ... 10 veces (por ejemplo) y Windows las va almacenando en un buffer, y aunque se libere la tecla, hace esas diez llamadas al callback...
¿Por qué ocurre con unas teclas y con otras no?

Y lo difícil:
¿Como puede ebitarse esto, para que en el programa AutoIt se ejecute el código de una tecla pulsada únicamente cuando esta lo está realmente? (como ocurre sin hacer nada, con Enter o Tab).

Se me ocurre jugar con variables, para comprobar si la tecla está pulsada, por ejemplo: Si lo está, asignarle 1. Si se libera, asignarle 0, pero no funcionaría, pues este comportamiento depende de Windows, así, que como no se pueda hacer algo con APIS... Me parece que no va a ser posible.

Salu2!
Responder