Página 1 de 1
Recibir tecla pulsada con GetKeyState
Publicado: 09 Sep 2009, 22:43
por Jonny
Hola
Siguiendo las explicaciones de Chefito en otro post donde hablábamos de GetKeyState, en ese caso para comprobar una tecla, tanto si estaba bloqueada, liverada o se pulsaba, Me he puesto a intentar darle otra utilidad a esta api.
Quiero recoger el código exadecimal de la tecla que se pulsa, como me dijo Chefito que podía hacerse si mal no entendí.
El problema, es que he estado buscando por Google pero solo encuentro la forma de comprobar el estado de una tecla, pero en ese caso hay que conocer el código de esta y por tanto no me sirve para éste caso en particular.
Se que esta información se recibe en el ´segundo elemento del array ([1]) pero no se que parámetro/s hay qe pasar a la función, en el lugar del código de la tecla a comprobar, por ejemplo...
Esto lo hago, porqqe necesitaría verificar digamos, todo el teclado... para comprobar cualquier tecla o convinación de teclas que se pulse, y si coincide con alguna establecida para el programa, que éste haga algo.
Gracias de antemano,
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 10 Sep 2009, 13:30
por Jonny
Hola
Bueno, bueno... Esta vez, he hecho los deberes...
Aunque no del todo.
He estado Googleando y he encontrado una solución al problema que planteaba en el post de arriba.
Se trata de la función GetKeyboardState, de la api de windows. Esta función nos devuelve un array de 256 elementos donde se almacena en cada posición el estado de cada tecla (Parece que la api trata teclas de varios idiomas y algunas posiciones ni siquiera son teclas)...
Tengo éste código:
Código: Seleccionar todo
Func _GetKeyboardState()
Local $Ret, $TState=DllStructCreate("byte[256]")
$Ret=DllCall("user32.dll", "int", "GetKeyboardState", "ptr", DllStructGetPtr($TState))
EndFunc
Como he dicho, teóricamente devuelve un array de 256 elementos pero no se ni donde, ni como...
He visto por el foro inglés algo como:
Código: Seleccionar todo
If BitAND(DllStructGetData($TState, 1, 1), 0x11)==1 Then
Return 1
EndIf
Pero con esto además de no entender muy bien la condición, no funciona.
¿Alguien sabe como tratar la respuesta de ésta api?
Gracias,
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 11 Sep 2009, 08:53
por Ximorro
No hay que complicarse tanto la vida. ¿No conocéis la función _IsPressed? Ya viene con AutoIt, está en misc.au3
Su utiliza haciendo
_IsPressed($codigotecla, 'user32.dll')
$codigotecla es un string con la representación en hexadecimal del código de la tecla, ganas de fastidiar, ya podían haberlo dejado en decimal. Tenéis los códigos en la ayuda de AutoIt para esta función.
La dll se recomienda meter en una variable si la función se va a usar mucho, para no hacer tantos re-enlaces.
La función devuelve TRUE si la tecla en cuestión está presionada y FALSE si no lo está. Estamos hablando de APRETAR en ese momento la tecla, no tiene que ver con mayúsculas activado o desactivado, por ejemplo, pues aunque esté activada si no la estás presionando dará FALSE.
Mi solución a tu cuestión inicial:
Código: Seleccionar todo
#include <Misc.au3>
Local $UserDLL = DllOpen("user32.dll"), $codigo, $i
for $i = 0 to 255
$codigo = StringRight(Hex($i),2) ;Pasamos a string hexadecial
If _IsPressed($codigo, $UserDLL) Then
ConsoleWrite("Dec: " & $i & ", Hexa: " & $codigo & @LF)
EndIf
Next
DllClose($UserDLL)
Por ejemplo si lo ejecuto mientras presiono F5, cursor derecha, y el botón secundario del ratón (SI, CON ESTO TAMBIEN PODEMOS CONTROLAR LOS BOTONES DEL RATON), en la consola me sale:
Dec: 2, Hexa: 02
Dec: 39, Hexa: 27
Dec: 116, Hexa: 74
Efectivamente, según la lista que hay en la ayuda (valores hexadecimales):
02 Right mouse button
27 RIGHT ARROW key
74 F5 key
En la lista esa sólo sale hasta "DD", y algunos códigos intermedios no tienen nada asignado. Por si acaso yo miro todo el rango, hasta 255 ("FF") aunque si sale uno que no está en la lista no sabemos cuál es. Si a alguien le sale algo nuevo que diga qué tecla estaba presionando y así conocemos más códigos...
Tampoco sé si 0 (cero) es un código válido para tecla, en la lista no aparece, pero como no da error en el bucle lo he puesto igual.
Espero que os sea de ayuda
Re: Recibir tecla pulsada con GetKeyState
Publicado: 11 Sep 2009, 09:38
por Ximorro
En el foro inglés veo la solución al otro problema, el del estado de las teclas bloqueables (mayúsculas, teclado numérico y tecla desplazamiento).
Es lo que encontraste, la función GetKeyState, GaryFrost da esta solución:
Código: Seleccionar todo
Global Const $VK_NUMLOCK = 0x90
Global Const $VK_SCROLL = 0x91
Global Const $VK_CAPITAL = 0x14
MsgBox(0,"Keys","NumLock: " & _GetNumLock() & @LF & _
"Scroll Lock: " & _GetScrollLock() & @LF & _
"Caps Lock: " & _GetCapsLock())
Func _GetNumLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_NUMLOCK)
Return $ret[0]
EndFunc
Func _GetScrollLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_SCROLL)
Return $ret[0]
EndFunc
Func _GetCapsLock()
Local $ret
$ret = DllCall("user32.dll","long","GetKeyState","long",$VK_CAPITAL)
Return $ret[0]
EndFunc
Probablemente con esta función también se pueda saber si está presionada la tecla, como lo de _IsPressed, entonces quizás se podría mirar todo a la vez, aunque son cosas diferentes...
Eso por lo visto más que 0 y 1 devuelve un entero largo, y a base de máscaras puedes sacar los diferentes tipos de estado, pero no sé cómo está estructurado eso...
Re: Recibir tecla pulsada con GetKeyState
Publicado: 11 Sep 2009, 12:27
por Jonny
Hola
Sí, conozco la función _IsPressed pero en éste caso en concreto no me sirve del todo.
Primero, porque necesito capturar el estado de todo el teclado y creo que sería más sencillo con esta api, ya que obtienes de un plumazo el estado de todo el teclado y de esta forma, puedes comprobar fácilmente tanto si se ha pulsado una tecla, como si se han pulsado varias a la vez...
Además, he observado que la función _IsPressed, habiendo según que aplicaciones activas (que hagan uso esplícito del teclado) es como si la tecla no funcionase, o no fuera detectada al pulsarla, por esa función.
He hecho el siguiente código con la función GetKeyboardState (La ffunción GetKeyState la domino, pues Chefito ya hizo algunos códigos con ella):
Código: Seleccionar todo
_GetKeyboardState()
Func _GetKeyboardState()
Local $Ret, $TState=DllStructCreate("byte[256]")
$Ret=DllCall("user32.dll", "int", "GetKeyboardState", "ptr", DllStructGetPtr($TState))
While 1
For $I=0x00 To 0xFF
If BitAND(DllStructGetData($TState, 1, $I), 0xF0) > 0 Then
Msgbox(0, "", "Pulsada: "&$I)
EndIf
Next
Wend
EndFunc
Esto como es de esperar, no funciona. Y esque, no me aclaro con éstos códigos (0xF0 etc)
Según ví en el foro inglés (de dónde saqué esta parte de código) lo van tratando con varias funciones, hasta quedar los códigos de las teclas en hexadecimal, que ya se comprende mejor...
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 11 Sep 2009, 13:36
por Ximorro
Con IsPressed ves el estado de todo el teclado, fíjate que como ejemplo yo estaba pulsando (F5+cursor derecho+botón derecho del ratón) y me ha dado los códigos de los tres, incluido el ratón. Y te pongo los códigos tanto en decimal como en hexadecimal...
¿De verdad no es lo que quieres?
Además lo veo tan sencillo como con la tuya (bueno el código que pones dice que no funciona, el mío sí
), en realidad si te fijas en tu bucle y en el mío son prácticamente iguales, sólo que yo en vez de estar comprobándolo constantemente sólo lo ejecuto una vez, si lo metiera en un bucle infinito como el tuyo serían iguales.
Otra cosa es que te falle con aplicaciones que capturan el teclado y no lo devuelven, aunque yo he probado IsPressed con éxito por ejemplo en juegos donde HotKeySet no funcionaba.
¿Con qué te falla IsPressed? ¿Y en esos casos GetKeyState sí funciona? Dime algún programa de esos a ver si tengo alguno y haré pruebas.
Ya que la dominas... ¿qué hace exactamente GetKeyState? ¿en qué me recomiendas usar esta en vez de IsPressed?
Ya todo esto ¿en qué se diferencian GetKeyState y GetKeyboardState?
Lo del BitAnd y la máscara F0 es porque estas funciones devuelven un número donde el estado está codificado en sus bits, y hay que aislar esos bits para ver dicho estado. Sabiendo qué bits son qué cosa te podemos explicar cómo montar los BitAnd.
En este caso F0 es en binario 11110000, así que está cogiendo la parte alta del byte bajo del número, y estás mirando si eso no es cero. La cuestión es qué se está codificando en esos 4 bits...
A ver si hay suerte y algún gurú de la API de Windows nos aclara la estructura de estos valores codificados.
Taluec!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 12 Sep 2009, 12:02
por Jonny
Hola
Vaya...
A mí siempre me ha dado mejores resultados HotKeySet que _IsPressed. En este caso, la diferencia es, que _IsPressed permite trabajar con cualquier tecla y convinaciónes de éstas, mientras qque HotKeySet según la ayuda de ésta función no permite tratar algunas teclas ni convinaciones de éstas, como por ejemplo Ctrl+Alt+supr y alguna más.
En cambio, HotKeySet me ha permitido capturar algunas teclas aún teniendo activas aplicaciones que hacían uso de las mismas, de una forma un tanto agresiva. Por ejemplo anulándolas en todo windows, para poder tratarlas dicha aplicación. Como te digo, lo ví sobre todo en videojuegos aunque hace mucho y ahora no sabría decirte en cual... (he probado con tantas aplicaciones)...
¿Qué hace GetKeyState?
Pues Te permite como bien digiste en otro post, verificar si una tecla (Cualquiera por lo visto) está bloqueada o desbloqueada (En el elemento 0 del array que devuelve) o, también permite comprobar igual que con _IsPressed, si la tecla está realmente pulsada (También en el elemento 0 del array que devuelve), pero en éste caso devuelve un valor distinto a 0 (desbloqueada) y 1 (bloqueada).
En el elemento 1 del array, según me dijo Chefito devuelve el código hexadecimal de la tecla pulsada.
La diferencia entre GetKeyState y GetKeyboardState, es que con GetKeyState puedes comprobar únicamente el estado de una tecla (puedes comprobar varias simultáneamente) pero haciendo una llamada por cada tecla que quieras comprobar y con GetKeyboardState tienes el estado de todo el teclado en un array, de una sola llamada a la función.
Pues, acabas de dejarme planchao con lo de los bits, me he quedao igualjeje... Me hago cada taco con eso que ... tela.
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 13 Sep 2009, 15:44
por Chefito
Y no te sirve el ejemplo de Yashied del foro de habla inglesa?
http://www.autoitscript.com/forum/index ... ntry708033
Tengo la impresión que el código lo has sacado de ese post.
Supongo que también se podría hacer con GetKeyState, comprobando el estado de todas las teclas con un for.....next.
Saludos.
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 07:00
por Jonny
Hola
Exactamente lo saqué de éste post, también suyo, aunque, mucho más complicado de entender (almenos para mí)
http://www.autoitscript.com/forum/index ... ntry651498
pero el código de ese post que pones está exactamente igual en ambos y no termino de entenderlo.
Por ejemplo, el switch ¿Qporqué tiene "to" como si fuera un for?...
y como dige, se me atragantan tanto bite ... esa forma de tratar las teclas con BitAnd, que por más que leo y leo la ayuda de esa función no comprendo ...
Pero sí, me sirve, de echo es lo que necesito...
Falta que funcione adaptándolo a mi programa :P.
Gracias,
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 10:00
por Ximorro
El switch es para saltarse los códigos no definidos. Con el "to" coge rangos, por ejemplo si haces
switch $i
case 1,3, 10 to 15
...
entrará en el case cuando $i valga 1, 3, 10, 11, 12, 13, 14 o 15.
Te prevengo que esa función no te dará varias teclas a la vez, si no la primera que encuentre. Es debido al "ExitLoop", en vez de salir tendrás que almacenarla por ejemplo en un Array y seguir mirando.
Pero insisto... mi ejemplo con IsPressed hace eso, ¿lo has probado?
Si lo que buscas es una interfaz gráfica, y que en vez de mirar sólo en el momento de ejecutar vaya comprobando cada cierto tiempo, mira éste, ejecuta y ponte a pulsar teclas (o botones de ratón) una o varias a la vez.
Se sale con la cruz de la ventana de título, he desactivado el ESC para que también salga su código.
Código: Seleccionar todo
#include <Misc.au3>
#include <GUIConstantsEx.au3>
Global $UserDLL = DllOpen("user32.dll"), $codigo, $i, $txt
Opt("GUIOnEventMode", 1)
Opt('GUICloseOnESC', 0)
GUICreate("Teclitas", 240, 100)
$lblTit = GUICtrlCreateLabel("Teclas presionadas (código hexadecimal):", 15, 20, 200, 52)
$lblCodigos = GUICtrlCreateLabel("", 20, 40, 160, 52)
GUISetOnEvent($GUI_EVENT_CLOSE, "_Salir")
GUISetState()
While 1
Sleep(50)
$txt = ""
for $i = 0 to 255
$codigo = StringRight(Hex($i),2) ;Pasamos a string hexadecial
If _IsPressed($codigo, $UserDLL) Then
$txt &= $codigo & " "
EndIf
Next
GUICtrlSetData($lblCodigos, $txt)
WEnd
Func _Salir()
DllClose($UserDLL)
Exit
EndFunc
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 10:58
por Ximorro
Qué curioso, jugando con el programita éste he visto que hay teclas que dan códigos múltiples, por lo visto es para dar un código genérico a la vez que el particular. Por ejemplo el Mayúsculas Izda da "10 A0" y el derecha "10 A1". Si miras la lista de teclas te pone:
10 SHIFT key
A0 Left SHIFT key
A1 Right SHIFT key
Así si quieres mirar cualquier Mayúsculas chequeas el código 0x10, lo activarán cualquiera de los dos; si quieres concretamente por ejemplo el derecho miras el 0xA1.
Una cosa rara es que el Alt se queda activado, tengo que volver a pulsar para desactivarlo..... se me ocurre que tendrá que ver con el menú, igual pierdo el foco al intentar abrirlo (la tecla ALT se va a los menús) y no se actualiza el label...
Una cosa, como los códigos salen ordenados, por ejemplo CTRL(izda) sale "11 A2", si hago CTRL+A, como la tecla A es 0x41, entonces la combinación me sale "11 41 A2", pues el 0x41 lo encuentra antes que el 0xA2, aunque 0x11 y 0xA2 sean activados a la vez por la misma tecla.
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 12:22
por Jonny
Hola
Efectivamente, eso iba a escribir (Te me adelantaste) xDD
Lo observé con el ctrl.
El código va bien, salvo por ejemplo, según que convinaciones de teclas solo se detectan si se pulsan simultáneamente, y no si ambas están presionadas, sin importar que una se haya pulsado antes que la otra... Supongo que habrá que jugar con el código para lograrlo.
He estado probando y probando, a ver si se pueden detectar todas las teclas independientemente de los programas famosos que capturan agresivamente el teclado y la verdad es, que detecta más teclas de las que esperaba, pero los cursores por ejemplo, en algunas situaciones no los detecta, si algún programa hace uso de ellos...
He estado jugando con un código que puso Chefito para anular el teclado (O teclas concretas) y con el tuyo pero de momento no he conseguido nada...
A ver si con la función GetKeyState y ésta puedo hacer algo, o como último recurso convinando éste código con Hotkeyset.
Una curiosidad:
¿Porqué abres user32.dll?
No veo que se haga uso de la dll en ninguna parte del código, más que cuando se comprueba si la tecla se ha pulsado. Es decir, en la función _IsPressed.
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 12:30
por Jonny
Hola
Bueno, mirando la ayuda de _IsPressed para consultar los códigos de las teclas, he visto el por qué abres user32.dll jeje.
No me había fijado en que era un parámetro que requiere la función...
Salu2!
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 13:46
por Ximorro
Se usa la dll porque por lo que veo IsPressed usa internamente GetAsyncKeyState, que será una variante de GetKeyState...
La meto en una variable para no re-engancharla en cada llamada, que se hacen muchas, al cerrar el programa hago un DllClose.
No sé qué quieres decir con "convinaciones de teclas solo se detectan si se pulsan simultáneamente, y no si ambas están presionadas"
¿"pulsar simultáneamente" y "ambas presionadas" no es lo mismo?
No sé muy bien lo que quieres decir, si pulsas CTRL+A, pulsando primero el CTRL, pues primero sale "11 A2", que es el CTRL izquierdo, luego al pulsar la A, como no sueltas el CTRL, pues te da los códigos "11 41 A2", que son el CTRL y la A a la vez, ¿no es eso lo que buscas?
Cuando luego sueltas la A, si por un momento aún mantienes CTRL pulsado pues volverá a aparecer "11 A2", esto no se acuerda de la combinación pulsada sino que mira en cada momento qué teclas hay pulsadas, sin acordarse de las combinaciones anteriores.
Una cosa que te puede pasar, y también puede afectar si hay otros programas mirando el teclado, es que lo compruebo cada 50ms. Lo hago para no hundir la CPU, es el Sleep(50). Si pulsas cuando está "dormido" pues no verá dicha pulsación. Prueba a poner números más pequeños, eso te dará más respuesta, pero también consumirá más CPU.
He hecho una versión que sólo actualiza la etiqueta si el estado ha cambiado, me parece más elegante pero el uso de CPU parece similar...
Y por cierto, en mi teclado en castellano tengo más códigos de los que ponen en la lista en inglés, que acaba en 0xDD, yo por ejemplo tengo el "acento agudo-diéresis-llave abierta" en 0xDE, supongo que los teclados QWERTY en español de los compañeros americanos serán iguales, pero la verdad es que no lo sé.
La Ñ me sale en 0xC0, que resulta que en la lista que tienen los ingleses se usa para ` (parece un acento grave). Si diferentes teclados usan mismos códigos para teclas diferentes puede ser un problema...
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 13:52
por Ximorro
Por cierto, te copio el código de IsPressed, sacado de la UDF misc.au3. Quizás puedas hacer una versión a partir de esta que te funcione mejor, no sé.
Código: Seleccionar todo
Func _IsPressed($sHexKey, $vDLL = 'user32.dll')
; $hexKey must be the value of one of the keys.
; _Is_Key_Pressed will return 0 if the key is not pressed, 1 if it is.
Local $a_R = DllCall($vDLL, "int", "GetAsyncKeyState", "int", '0x' & $sHexKey)
If Not @error And BitAND($a_R[0], 0x8000) = 0x8000 Then Return 1
Return 0
EndFunc ;==>_IsPressed
A lo mejor si en vez de usar GetAsyncKeyState se usa GetKeyState va mejor, pruébalo y nos dices...
Re: Recibir tecla pulsada con GetKeyState
Publicado: 14 Sep 2009, 16:58
por Jonny
Hola
Bueno, el problema de tener controlado el maldito alt+Tab, resuelto.
Muchas gracias Ximorro, era tan sencillo como:
Código: Seleccionar todo
If (_IsPressed(12, $KeyboardHand)==1 And _IsPressed(09, $KeyboardHand)==1) Then
Funciona a la perfección. Tanto pulsando Alt y sin soltarla Tabulador, como las dos a la vez ...
(Era a eso a lo que me refería antes) jeje.
No se por qué, no me funcionó aplicándolo a tu ejemplo. En cualquier caso, que ya se que no tiene que tener nada que ver, pero me ha funcionado aplicándolo al programa que estoy haciendo, que al simular multihilo en vez de en un bucle, lo he puesto en un timer (como dato extra) xdd.
De saberlo, hubiera usado antes esta función para esto. No lo hice antes, porque en alguna versión más antigua de Autoit no me respondía tan bien como ahora. Y muchas de las teclas que ahora sí detecta en los casos en que otro programa detecta el estado del teclado, antes no lo hacía.
En cualquier caso, tanto tú como Chefito me habeis ayudado bastante, porque convinando _IsPressed y GetKeyState puedo controlar (ahora sí) todo el teclado.
Así pues, la función GetKeyboardState queda descartada :P.
Sí, se que comprobando la pulsación de Alt+Tab no voy a interrumpir la operación que tenga asignada Windows para ese hotkey... Gracias a dios, no se me ocurrió hacerlo jejeje (Si esque se pudiera, eso sí que sería un buen tinglao) :P.
Gracias,
Salu2!