Simuler la frappe d'une touche du clavier
Rien de plus simple en principe pour simuler en VBA la frappe d'une (ou plusieurs) touche(s) du clavier, Sendkeys est fait pour cela :
Sendkeys "zaza"
va ainsi simuler la frappe au clavier de "zaza".
Sendkeys "zaza", True
va faire la même chose, mais attendre que toutes les lettres aient été envoyées avant de laisser la macro se poursuivre.
Principale difficulté, Sendkeys a une facheuse tendance à inscrire le message n'importe où.
Il est très important d'activer l'application et la fenêtre qui doit recevoir les instructions du clavier.
Par exemple, faire précéder SendKeys de AppActivate en VBA permet d'éviter d'inscrire le message à l'intérieur du module si la macro est lancée depuis VBE !
AppActivate "Microsoft Excel - " & ThisWorkbook.Name
Sheets(2).Select
Range("B5").Select
SendKeys "zaza~", True
SendKeys peut également simuler l'action de touches spécifiques, par exemple SendKeys("{ENTER}") ou SendKeys("~") simule l'action de la touche "ENTREE" (voir l'aide de SendKeys pour les autres touches, y compris la combinaison de plusieurs touches).
On peut également utiliser la fonction SendKeys du Scripting Host, qui se comporte à peu près pareil (mais est parfois plus fiable ?) :
Set sh = CreateObject("WScript.Shell")
sh.Run "notepad"
sh.AppActivate "Bloc-notes"
sh.SendKeys "zaza~"
Set sh = Nothing
On peut aussi utiliser la fonction API keybd_event de la dll user32 :
keybd_event vbKeyZ envoie "z"
keybd_event vbKeyReturn simule la frappe de "Entrée"
(pour voir la liste des touches, cliquez ici).
Private Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Sub ecrit(truc)
For num = 1 To Len(truc)
keybd_event Asc(Mid(UCase(truc), num, 1)), 0, 0, 0 'enfoncer la touche
keybd_event Asc(Mid(UCase(truc), num, 1)), 0, 2, 0 'relacher la touche
Next
End Sub
Il suffit de lancer la macro ecrit("zaza") pour simuler la frappe des touches Z,A,Z et A.
La méthode la plus fiable consiste à envoyer le message de manière spécifique à une fenêtre en utilisant les API (FindWindowA, GetWindow, PostMessageA et SetForegroundWindow de user32) :
Declare Function GetWindow Lib "user32" _
(ByVal hwnd As Long, ByVal wCmd As Long) As Long
Declare Function cherche_fenetre Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
Public Const VK_RETURN = &HD
Public Const WM_KEYDOWN = &H100
Sub ecrire_dans_notepad()
'envoyer un message sur notepad
nom_fenetre = "Sans titre - Bloc-notes"
notepad = cherche_fenetre(vbNullString, nom_fenetre)
notepad = GetWindow(notepad, 5) 'se placer dans la fenetre du texte
txt = "BONJOUR TOUT LE MONDE"
For num = 1 To Len(txt)
PostMessage notepad, WM_KEYDOWN, Asc(Mid(txt, num, 1)), 0
Next
PostMessage notepad, WM_KEYDOWN, VK_RETURN, 0 'enter
SetForegroundWindow (notepad)
End Sub
La macro va rechercher le handle de la fenetre NotePad "sans-titre" et y inscrire un texte puis passer à
la ligne. Ensuite seulement, elle affiche la fenêtre (pour voir la liste des touches, cliquez ici).
Cette technique remplace avantageusement SendKeys : elle évite d'envoyer par erreur des commandes sur
une fenetre non visée, et elle permet d'envoyer des commandes sur une fenêtre qui n'est pas active.
(on aurait aussi pu utiliser SendMessage qui permet d'attendre que les commandes soient transmises avant de
poursuivre le programme).
Enfin, on peut également utiliser les API SendInput et CopyMemory :
Type KEYBDINPUT
wVk As Integer
wScan As Integer
dwFlags As Long
time As Long
dwExtraInfo As Long
End Type
Type HARDWAREINPUT
uMsg As Long
wParamL As Integer
wParamH As Integer
End Type
Type GENERALINPUT
dwType As Long
xi(0 To 23) As Byte
End Type
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long, pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
Dim genInput(0 To 1) As GENERALINPUT
Dim keyInput As KEYBDINPUT
Private Sub taper(touche As Byte)
For nn = 0 To 1 '0 pour keydown, 1 pour keyup
keyInput.wVk = touche 'définit la touche à actionner
keyInput.dwFlags = 2 * nn 'presse ou relache la touche
genInput(nn).dwType = 1 'action clavier
CopyMemory genInput(nn).xi(0), keyInput, Len(keyInput)
Next nn
Call SendInput(2, genInput(0), Len(genInput(0))) 'envoi de l'input
End Sub
Sub ecrit()
mot = "BONJOUR ZAZA"
For num = 1 To Len(mot)
taper Asc(Mid(mot, num))
Next
End Sub
(pour voir la liste des touches, cliquez ici)
Simuler la frappe d'une touche est souvent un exercice périlleux.
Le fait de disposer de plusieurs méthodes
fonctionnant sur des bases différentes permet souvent de régler des problèmes embarrassants...