Visual Basic, VB .NET, ASP, Active X, Access, SQL Server

VB: SendKeys usando API

O VB fornece o comando SendKeys usado para criar eventos de teclado no controle que tem o foco. Contudo, na prática, Sendkeys às vezes não funciona corretamente, nem todas as teclas podem ser enviadas e não oferece muita flexibilidade no controle da sequência de digitações que são enviadas. Este artigo mostra como usar a função da API keybd_event para superar estas limitações.

Sobre keybd_event

A função keybd_event produz o efeito de digitações no teclado; é, essencialmente, a mesma rotina chamada pelo driver do teclado para gerar eventos reais de digitações. Embora a função tenha sido superada no NT e em versões 9x mais modernas pela função SendInput, ela continua a ser implementada em todos os sistemas.

keybd_event recebe dois parâmetros; o código de tecla virtual para simular um evento de teclado e um flag indicando se o evento é de elevação de tecla (key up) ou de pressionamento de tecla (key down).

Há duas maneiras de atribuir o código de tecla virtual. Primeiramente, você pode usar a constante de tecla virtual diretamente. O VB contém constantes para a maior parte dos códigos de teclas virtuais na enumeração KeyCodeConstants. Contudo, há um número de teclas faltantes, tais como a tecla para o botão direito do mouse no teclado. Ou então, você pode usar a função da API VkKeyScan para obter o código de tecla para um caractere em particular. 

Juntando isto, nós podemos escrever funções simples para criar eventos de KeyUp e KeyDown:

Private Declare Sub keybd_event Lib "user32" ( _
   ByVal bVk As Byte, ByVal bScan As Byte, _
   ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_EXTENDEDKEY = &H1
Private Const KEYEVENTF_KEYUP = &H2

Private Declare Function GetVersion Lib "kernel32" () As Long
Private Declare Function VkKeyScan Lib "user32" Alias "VkKeyScanA" ( _
   ByVal cChar As Byte) As Integer
Private Declare Function VkKeyScanW Lib "user32" ( _
   ByVal cChar As Integer) As Integer

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)


Public Sub KeyDown(ByVal vKey As KeyCodeConstants)
   keybd_event vKey, 0, KEYEVENTF_EXTENDEDKEY, 0
End Sub

Public Sub KeyUp(ByVal vKey As KeyCodeConstants)
   keybd_event vKey, 0, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0
End Sub

Public Function KeyCode(ByVal sChar As String) As KeyCodeConstants
Dim bNt As Boolean
Dim iKeyCode As Integer
Dim b() As Byte
Dim iKey As Integer
Dim vKey As KeyCodeConstants
Dim iShift As ShiftConstants

   ' Determina se nós temos suporte Unicode ou não:
   bNt = ((GetVersion() And &H80000000) = 0)
   
   ' Obtém o código de teclado para o caractere
   If (bNt) Then
      b = sChar
      CopyMemory iKey, b(0), 2
      iKeyCode = VkKeyScanW(iKey)
   Else
      b = StrConv(sChar, vbFromUnicode)
      iKeyCode = VkKeyScan(b(0))
   End If
   
   KeyCode = (iKeyCode And &HFF&)

End Function

Podendo gerar eventos key down e key up independentemente,podemos enviar digitações de forma mais eficiente. Contudo, você tem de cuidar de formar pares de eventos key up e key down, principalmente com as teclas Control, Alt e Shift, caso contrário seu computador poder ficar difícil de usar!

Demonstração

Para demonstrar o uso do código acima, crie um novo projeto com um formulário, uma caixa de texto Text1 e um botão Command1. Adicione um módulo de classe e adicione-o ao módulo o código acima. Dê a este módulo o nome de cSendKeys. No evento Click do botão adicione o código abaixo para produzir a digitação da letra "A":

Text1.SetFocus
Dim c As New cSendKeys
c.KeyDown vbKeyShift            'pressiona a tecla Shift
c.KeyDown c.KeyCode("A")     'pressiona a tecla A
c.KeyUp c.KeyCode("A")         'solta a tecla A
c.KeyUp vbKeyShift                 'libera a tecla Shift

Neste exemplo tivemos que chamar as rotinas KeyDown para pressionar a tecla e KeyUp para liberá-la. Para poupar-nos deste trabalho, podemos embutir as duas chamadas em um novo método da classe que faça as duas chamadas para nós.

Use esta classe em substituição à função SendKeys do VB. A diferença é que você terá sempre que criar antes uma instância da classe para poder chamar seus métodos.