Foros del Web » Programación para mayores de 30 ;) » .NET »

Simular teclado VB.Net

Estas en el tema de Simular teclado VB.Net en el foro de .NET en Foros del Web. Hola, buenas a todos. Me propusieron un problema muy interesante pero no consigo dar con la solución. Estoy trabajando con VS2008 usando VB.NET Net Framework ...
  #1 (permalink)  
Antiguo 02/03/2010, 16:06
Avatar de xdrtas  
Fecha de Ingreso: junio-2008
Ubicación: Venezuela
Mensajes: 97
Antigüedad: 15 años, 10 meses
Puntos: 13
Simular teclado VB.Net

Hola, buenas a todos. Me propusieron un problema muy interesante pero no consigo dar con la solución. Estoy trabajando con VS2008 usando VB.NET Net Framework 3.5 bajo windows 7.

La idea es la siguiente: enviar por medio de una aplicación las pulsaciones de teclado a una aplicación externa.

Cuál es el problema? Cuando se trabaja con aplicaciones que reciben texto no hay problema, pero supongamos que la aplicación tiene varias ventanas, una ventana donde se muestra una imagen, otra ventana con accesos directos de teclado, otra con iconos, etc..., estas ventanas no reciben texto sino pulsaciones de teclado. Estas pulsaciones son lo que se denomina "hotKey".

Bien, he problado con:
  1. SendKeys.Send() pero no funciona salvo para la ventana que recibe texto, si ésta ventana está activa.
  2. API Keybd_event pero hace exactamente lo mismo que el anterior.
  3. He usado los eventos KeyDown, KeyUp, etc, de los controles que van a enviar el hotkey y tampoco funciona.
  4. Probé también pasar el caracter a byte con la función ASC pero me hace lo mismo.
  5. Probé al enviar el hotket con keys.[El caracter o tecla de función que necesite], como por ejemplo "Keys.LControlKey" o "Keys.Enter" pero obtengo los mismos resultados.

Con la curiosidad probé la aplicación que trae el windows 7 "Teclado en Pantalla" y "voila" ahi si sirve todo sobre la aplicación externa.

Entonces lo que necesito es saber como rayos trabaja el teclado en pantalla del windows, supongo que estará programado en C++ o C#, pero aun así, usará un link a una DLL de una API para trabajar el teclado a bajo nivel para conseguir lo que quiero.

Una cosa más, cuando uso "SendKeys.Send("{ENTER}") si funciona en la aplicación externa, pero si hago esto "SendKeys.Send("T")" no funciona como hotkey excepto si la ventana para recibir el texto está activa.

En otras palabras los caracteres los recibe como texto cuando los envio por SendKeys.Send o cuando lo envio usando Keybd_event, pero no como pulsación del teclado. Esto no pasa con el "teclado en pantalla" del windows, que cuando le doy a T del teclado en pantalla éste si funciona como hotKey.

Cualquier información que puedan pasarme sobre el trabajo a bajo nivel de teclado por medio de VB.NET se los agradeceré.

Les adjunto el código de lo que tengo hecho en VB.NET para que vean.

Código VB.NET:
Ver original
  1. Imports System.Runtime.InteropServices
  2.  
  3. Public Class Form1
  4.     Const KEYEVENTF_KEYUP = &H2
  5.     Const KEYEVENTF_EXTENDEDKEY = &H1
  6.  
  7.     <DllImport("user32.dll", EntryPoint:="keybd_event", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
  8.     Public Shared Sub Keybd_event(ByVal vk As Byte, ByVal scan As Byte, ByVal flags As Integer, ByVal extrainfo As Integer)
  9.     End Sub
  10.  
  11.     Declare Function VkKeyScan Lib "user32" Alias "VkKeyScanA" (ByVal cChar_Renamed As Byte) As Short
  12.  
  13.     Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
  14.         sTimer(TextBox2, TextBox1)
  15.     End Sub
  16.  
  17.     Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
  18.         sTimer(TextBox4, TextBox3)
  19.     End Sub
  20.  
  21.     Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
  22.         sTimer(TextBox6, TextBox5)
  23.     End Sub
  24.  
  25.     Private Sub Timer4_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer4.Tick
  26.         sTimer(TextBox8, TextBox7)
  27.     End Sub
  28.  
  29.     Private Sub Timer5_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer5.Tick
  30.         sTimer(TextBox10, TextBox9)
  31.     End Sub
  32.  
  33.     Private Sub Timer6_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer6.Tick
  34.         sTimer(TextBox12, TextBox11)
  35.     End Sub
  36.  
  37.     Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
  38.         Timer1.Enabled = True
  39.         Timer2.Enabled = True
  40.         Timer3.Enabled = True
  41.         Timer4.Enabled = True
  42.         Timer5.Enabled = True
  43.         Timer6.Enabled = True
  44.     End Sub
  45.  
  46.     Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
  47.         Timer1.Enabled = False
  48.         Timer2.Enabled = False
  49.         Timer3.Enabled = False
  50.         Timer4.Enabled = False
  51.         Timer5.Enabled = False
  52.         Timer6.Enabled = False
  53.     End Sub
  54.  
  55.     Private Sub sTimer(ByVal txt As TextBox, ByVal txtEnvio As TextBox)
  56.         'Se comprueba que exista texto en la caja correspondiente y que su valor sea mayor que 0...
  57.         If Not (txt.Text = "") And IsNumeric(txt.Text) Then
  58.             If CLng(txt.Text) > 0 Then
  59.                 txt.Text = CStr(CLng(txt.Text) - 1)
  60.             End If
  61.         Else
  62.             Exit Sub
  63.         End If
  64.  
  65.         Dim time_t As Integer
  66.         time_t = CInt(txt.Text)             'Se transforma el valor de la caja de texto al tipo integer...
  67.  
  68.         If CInt(txt.Text) = 0 Then
  69.             txt.Text = time_t.ToString()    'El valor del time_t se pasa a string y se le asigna a la caja de texto...
  70.         End If
  71.  
  72.         Dim tiempomuerto As Integer
  73.         tiempomuerto = 1
  74.  
  75.         If CInt(txt.Text) = tiempomuerto Then   'Si el valor es igual a 1 entonces...
  76.             'MsgBox(Asc(txtEnvio.Text), MsgBoxStyle.OkOnly, "Prueba")
  77.             SendKeys.Send(Keys.Enter)
  78.             'Pulsar(CByte(Asc(txtEnvio.Text))) 'se envía lo que exista en la caja de texto...    
  79.         End If
  80.     End Sub
  81.  
  82.     Private Sub Pulsar(ByVal tecla As Byte)
  83.  
  84.         Keybd_event(tecla, 0, 0, 0)
  85.         Keybd_event(tecla, 0, KEYEVENTF_KEYUP, 0)
  86.  
  87.     End Sub
  88. End Class

Es un formulario con 12 cajas de texto y 6 timer, pero con 2 cajas y un timer bastará para probar.

La caja de texto par, (textbox2, textbox4, textbox6, etc), es donde se coloca el tiempo antes de que se envíe el hotkey, (va en cuenta regresiva).
La caja de texto impar, (textbox1, textbox3, textbox5, etc), es donde se coloca el caracter que será el hotkey a enviar a la aplicación externa.
Button3, empieza la cuenta regresiva, Button4 detiene los timers.

Saludos y muchas gracias de antemano.
__________________
¿Cuál es el mejor lenguaje para programar?
Aquel lenguaje por el cual te paguen más.
[--::xdrtas.coolpage.biz::--]
[---:::xdrtas:::---]
  #2 (permalink)  
Antiguo 06/03/2010, 23:08
Avatar de xdrtas  
Fecha de Ingreso: junio-2008
Ubicación: Venezuela
Mensajes: 97
Antigüedad: 15 años, 10 meses
Puntos: 13
Respuesta: Simular teclado VB.Net

Un tema difícil de verdad. Pero ya encontré una solución. Bien un poco de teoría primero.

Lo que intentaba hacer era enviar pulsaciones de teclado a un juego.
¿Cuál es el problema? El problema consiste, por lo que pude averiguar, que los juegos como este que trabajan bajo DirectX tiene su propio nivel de pulsaciones de teclado y desde el método SendKeys.send() no se puede enviar a este tipo de aplicaciones, (esto no es del todo cierto ya que funciona cuando se envía el siguiente código: SendKeys.Send("{ENTER}")).

Lo siguiente es una suposición mia, por favor no lo tomen como referencia y es sobre el "{ENTER}", creo yo que en el código del evento o de la clase SendKeys, el comando "{ENTER}" está programado a bajo nivel, ya sea porque está codificado con su valor hexadecimal y no ASCII o ya sea porque para este caso en especial quisieron que el enter fuera a cualquier aplicación. Saqué esta conclusión debido a un programa en C donde se usaba SendInput y el enter lo enviaban en hexadecimal y el resto de las teclas en su correspondiente valor ASCII o sea en byte. Como les he dicho, NO TOMEN ESTA CONCLUSIÓN COMO REFERENCIA, realmente no conozco como funciona internamente el envío de pulsaciones de teclas del framework de .NET y puedo estar totalmente equivocado, si alguien sabe el porque del funcionamiento del "{ENTER}" y no del resto de las teclas les agradecería la información.

Encontré información que habla sobre importar la librería Microsoft.DirectX.DirectInput que se encarga de trabajar con el teclado, ratón joystic, etc... para DirectX, pero el problema es que no conseguí información de cómo enviar una pulsación de tecla, (bueno, encontré algo, pero haciendo honor a la verdad, no entendí nada, ni siquiera estoy seguro de que realmente se envíe la tecla).
Así que desistí del tema del uso de directX, por ahora claro, y fuí por otro camino.

Buscando en interner conseguí está aplicación AutoIt v3 que sirve para crear macros, lo que me llamó la atención al leer la documentación, es que se podía usar la librería en los proyectos .NET y es gratuito.

Instalé la aplicación, hice referencia a la librería en mi proyecto y luego probé el método para enviar la pulsación al juego, el resultado es que funciona perfecto.

Ahora lo divertido:
  1. La aplicación se llama AutoIt v3 - San Google se los encontrará en un 2 x 3.
  2. La librería se llama AutoItX3Lib.dll. Importar la librería al proyecto: imports AutoItX3Lib
  3. Instanciamos: Dim au As New AutoItX3Lib.AutoItX3
  4. Uso del envío de pulsaciones: au.Send("t up",0) o también au.Send("t down",0)

Código VB.NET:
Ver original
  1. Imports AutoItX3Lib
  2.  
  3. Public Class Form1
  4.     Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
  5.         sTimer(TextBox2, TextBox1)
  6.     End Sub
  7.  
  8.     Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
  9.         sTimer(TextBox4, TextBox3)
  10.     End Sub
  11.  
  12.     Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
  13.         sTimer(TextBox6, TextBox5)
  14.     End Sub
  15.  
  16.     Private Sub Timer4_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer4.Tick
  17.         sTimer(TextBox8, TextBox7)
  18.     End Sub
  19.  
  20.     Private Sub Timer5_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer5.Tick
  21.         sTimer(TextBox10, TextBox9)
  22.     End Sub
  23.  
  24.     Private Sub Timer6_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer6.Tick
  25.         sTimer(TextBox12, TextBox11)
  26.     End Sub
  27.  
  28.     Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
  29.         Timer1.Enabled = True
  30.         Timer2.Enabled = True
  31.         Timer3.Enabled = True
  32.         Timer4.Enabled = True
  33.         Timer5.Enabled = True
  34.         Timer6.Enabled = True
  35.     End Sub
  36.  
  37.     Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
  38.         Timer1.Enabled = False
  39.         Timer2.Enabled = False
  40.         Timer3.Enabled = False
  41.         Timer4.Enabled = False
  42.         Timer5.Enabled = False
  43.         Timer6.Enabled = False
  44.     End Sub
  45.  
  46.     Private Sub sTimer(ByVal txt As TextBox, ByVal txtEnvio As TextBox)
  47.         On Error Resume Next
  48.         'Se comprueba que exista texto en la caja correspondiente y que su valor sea mayor que 0...
  49.         If Not (txt.Text = "") And IsNumeric(txt.Text) Then
  50.             If CLng(txt.Text) > 0 Then
  51.                 txt.Text = CStr(CLng(txt.Text) - 1)
  52.             End If
  53.         Else
  54.             Exit Sub
  55.         End If
  56.  
  57.         Dim time_t As Integer
  58.         time_t = CInt(txt.Text)             'Se transforma el valor de la caja de texto al tipo integer...
  59.  
  60.         If CInt(txt.Text) = 0 Then
  61.             txt.Text = time_t.ToString()    'El valor del time_t se pasa a string y se le asigna a la caja de texto...
  62.         End If
  63.  
  64.         Dim tiempomuerto As Integer
  65.         tiempomuerto = 0
  66.  
  67.         If CInt(txt.Text) = tiempomuerto Then   'Si el valor es igual a 1 entonces...
  68.             Dim au As New AutoItX3Lib.AutoItX3  'Librería encargada de enviar las pulsaciones del teclado...
  69.  
  70.             au.Send(txtEnvio.Text + " down", 0) 'Con esta línea simulamos que estamos presionando la tecla..
  71.             'au.Send(txtEnvio.Text + " up", 0)   'Con esta línea simulamos que estamos soltando la tecla presionada...
  72.         End If
  73.     End Sub
  74. End Class

au.Send(txtEnvio.text + " down") simula que se está presionando sobre la tecla. Es impor tante colocar ya sea el "down" o el "up", si no, no va a funcionar.

Y por último, esta librería también permite activar la ventana, minimizar, cerrar, abrir aplicaciones, etc...
Todo lo que una librería para macros debería hacer, solo hay que investigarla un poco y hacer MUCHAS PRUEBAS .

Un cordial saludo a todos y que lo disfruten.
__________________
¿Cuál es el mejor lenguaje para programar?
Aquel lenguaje por el cual te paguen más.
[--::xdrtas.coolpage.biz::--]
[---:::xdrtas:::---]

Etiquetas: simular, teclado
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 1 personas




La zona horaria es GMT -6. Ahora son las 13:47.