Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Simuler la frappe clavier sur une autre fenetre

9 réponses
Avatar
Olivier Miakinen
Bonjour,

J'ai à maintenir un programme C++ qui simule la frappe clavier sur une
autre fenêtre. Par exemple, ayant détecté une fenêtre qui demande un nom
de login et un mot de passe, c'est le programme en question qui saisit
les informations à la place de l'utilisateur.

Mon problème, c'est que le programme est sensible à l'état de certaines
touches du vrai clavier (par exemple Shift ou Caps Lock), alors que
j'aimerais que ça ne soit pas le cas.

Le code actuel utilise les fonctions suivantes ;
VkKeyScan(...)
GetKeyboardState(...)
SetKeyboardState(...)
MapVirtualKey(...)
PostMessage(... WM_KEYDOWN...)
PostMessage(... WM_KEYUP...)
Je ne publie pas le code, à moins que ce ne soit vraiment nécessaire.

En fait, j'ai fait des recherches sur Internet, et vu quelqu'un
déconseiller les PostMessage(... WM_KEYDOWN...) et PostMessage(...
WM_KEYUP...) en faveur de keybd_event(), et du coup en lisant la
doc j'ai vu que SendInput() était conseillé à la place.

Bref, je n'y comprends rien, et j'aimerais savoir s'il existe quelque
part un exemple de code qui marche et que je pourrais repomper.

Cordialement,
--
Olivier Miakinen

9 réponses

Avatar
Christian ASTOR
On 21 fév, 16:23, Olivier Miakinen <om+ wrote:

En fait, j'ai fait des recherches sur Internet, et vu quelqu'un
déconseiller les PostMessage(... WM_KEYDOWN...) et PostMessage(...
WM_KEYUP...) en faveur de keybd_event(), et du coup en lisant la
doc j'ai vu que SendInput() était conseillé à la place.

Bref, je n'y comprends rien, et j'aimerais savoir s'il existe quelque
part un exemple de code qui marche et que je pourrais repomper.



Oui, il faut utiliser keybd_event() ou SendInput()
(après avoir mis la fenêtre en foreground, avec SwitchToThisWindow()
par ex)

Un vieil ex de fonction pour simuler du texte =>

void GenerateText(char* sText)
{
bool bShift, bControl, bAlt;
unsigned int nPos, nCpt;
char cChar;
short nKeyScan;
INPUT input[256];
for (nPos=0;nPos<= strlen(sText)-1;nPos++)
{
ZeroMemory(input, sizeof input);
nCpt = 0;
cChar = sText[nPos];
nKeyScan = VkKeyScan(cChar);
bShift = (HIBYTE(nKeyScan) & 1?1:0);
bControl = (HIBYTE(nKeyScan) & 2?1:0);
bAlt = (HIBYTE(nKeyScan) & 4?1:0);
if (bShift)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_SHIFT;
input[nCpt].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
nCpt++;
}
if (bControl)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_CONTROL;
input[nCpt].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
nCpt++;
}
if (bAlt)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_MENU;
input[nCpt].ki.wScan = MapVirtualKey(VK_MENU, 0);
nCpt++;
}
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = LOBYTE(nKeyScan);
input[nCpt].ki.wScan = MapVirtualKey(LOBYTE(nKeyScan), 0);
nCpt++;
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = LOBYTE(nKeyScan);
input[nCpt].ki.wScan = MapVirtualKey(LOBYTE(nKeyScan), 0);
input[nCpt].ki.dwFlags = KEYEVENTF_KEYUP;
nCpt++;
if (bShift)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_SHIFT;
input[nCpt].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
input[nCpt].ki.dwFlags = KEYEVENTF_KEYUP;
nCpt++;
}
if (bControl)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_CONTROL;
input[nCpt].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
input[nCpt].ki.dwFlags = KEYEVENTF_KEYUP;
nCpt++;
}
if (bAlt)
{
input[nCpt].type = INPUT_KEYBOARD;
input[nCpt].ki.wVk = VK_MENU;
input[nCpt].ki.wScan = MapVirtualKey(VK_MENU, 0);
input[nCpt].ki.dwFlags = KEYEVENTF_KEYUP;
nCpt++;
} SendInput(nCpt, input, sizeof INPUT);
}
}
Avatar
Olivier Miakinen
Le 21/02/2008 16:42, Christian ASTOR a écrit :

Oui, il faut utiliser keybd_event() ou SendInput()
(après avoir mis la fenêtre en foreground, avec SwitchToThisWindow()
par ex)



Je ne connaissais pas cette fonction, qui peut m'être très utile en
effet. Malgré tout, la doc m'inquiète un peu :
<cit. http://msdn2.microsoft.com/en-us/library/ms633553(VS.85).aspx>
However, this function is deprecated and not intended for general use.
It is recommended that you do not use it in new programs because it
might be altered or unavailable in subsequent versions of Windows.
</>

Est-ce que l'une des options de ShowWindow() ferait la même chose, par
exemple SW_RESTORE ou SW_SHOW ?

Un vieil ex de fonction pour simuler du texte =>

void GenerateText(char* sText)
{
[...]
}



Un grand merci pour cet exemple !
Avatar
Christian ASTOR
On 21 fév, 17:00, Olivier Miakinen <om+ wrote:

However, this function is deprecated



MSDN met souvent ça, alors que les apis sont utilisées par Windows lui-
même (taskmgr entre autres pour cette api)
Mais si le prog a déjà, comme cité, "détecté une fenêtre qui dem ande
un nom de login et un mot de passe", c'est peut-être inutile si le
focus est déjà mis.
Avatar
Olivier Miakinen
Le 21/02/2008 17:40, Christian ASTOR a écrit :

However, this function is deprecated



MSDN met souvent ça, alors que les apis sont utilisées par Windows lui-
même (taskmgr entre autres pour cette api)



:-D

Mais si le prog a déjà, comme cité, "détecté une fenêtre qui demande
un nom de login et un mot de passe", c'est peut-être inutile si le
focus est déjà mis.



Cela pourrait me servir dans un cas où l'envoi des caractères se fait
très lentement, et qu'il est possible de perdre le focus avant la fin.
Avatar
Olivier Miakinen
Le 21/02/2008 16:42, Christian ASTOR a écrit :

Un vieil ex de fonction pour simuler du texte =>

void GenerateText(char* sText)
{
[...]
}



Ça ne marche pas. :-(

Plus exactement, cette fonction fait bien ce qu'il faut quand aucune
touche n'est enfoncée et que le voyant Caps Lock est éteint, mais pas
dans le cas contraire. Par exemple,

Voici le code que j'appelle pour essayer d'annuler ça, d'abord
_SaveAndResetKeyboardState() avant d'appeler ta fonction, puis
_RestoreKeyboardState() quand c'est terminé :

#define KEYPRESS(input,idx,key) {
input[idx].type = INPUT_KEYBOARD;
input[idx].ki.wVk = key;
input[idx].ki.wScan = MapVirtualKey(key, 0);
input[idx].ki.dwFlags = 0;
idx++;
}
#define KEYRELEASE(input,idx,key) {
input[idx].type = INPUT_KEYBOARD;
input[idx].ki.wVk = key;
input[idx].ki.wScan = MapVirtualKey(key, 0);
input[idx].ki.dwFlags = KEYEVENTF_KEYUP;
idx++;
}
static BOOL _SaveAndResetKeyboardState(BYTE *bKbState)
{
::GetKeyboardState(bKbState);

INPUT input[9];
int idx = 0;
ZeroMemory(input, sizeof input);
if (bKbState[VK_CAPITAL] & 0x80) {
// Touche Caps lock enfoncée : on la relâche
KEYRELEASE(input, idx, VK_CAPITAL);
}
if (bKbState[VK_CAPITAL] & 0x01) {
// Voyant Caps lock allumé : on l'éteint
KEYPRESS(input, idx, VK_CAPITAL);
KEYRELEASE(input, idx, VK_CAPITAL);
}
if (bKbState[VK_LSHIFT] & 0x80) {
// Touche Shift gauche enfoncée : on la relâche
KEYRELEASE(input, idx, VK_LSHIFT);
}
if (bKbState[VK_RSHIFT] & 0x80) {
// Touche Shift droit enfoncée : on la relâche
KEYRELEASE(input, idx, VK_RSHIFT);
}
if (bKbState[VK_LCONTROL] & 0x80) {
// Touche Ctrl gauche enfoncée : on la relâche
KEYRELEASE(input, idx, VK_LCONTROL);
}
if (bKbState[VK_RCONTROL] & 0x80) {
// Touche Ctrl droit enfoncée : on la relâche
KEYRELEASE(input, idx, VK_RCONTROL);
}
if (bKbState[VK_LMENU] & 0x80) {
// Touche Alt gauche enfoncée : on la relâche
KEYRELEASE(input, idx, VK_LMENU);
}
if (bKbState[VK_RMENU] & 0x80) {
// Touche Alt droit enfoncée : on la relâche
KEYRELEASE(input, idx, VK_RMENU);
}
SendInput(idx, input, sizeof INPUT);
return TRUE;
}
static BOOL _RestoreKeyboardState(BYTE *bKbState)
{
INPUT input[9];
int idx = 0;
ZeroMemory(input, sizeof input);
switch (bKbState[VK_CAPITAL] & 0x81) {
case 0x81:
// La touche était enfoncée, le voyant allumé
KEYPRESS(input, idx, VK_CAPITAL);
break;
case 0x01:
// La touche était relâchée, le voyant allumé
KEYPRESS(input, idx, VK_CAPITAL);
KEYRELEASE(input, idx, VK_CAPITAL);
break;
case 0x80:
// La touche était enfoncée, le voyant éteint
KEYPRESS(input, idx, VK_CAPITAL);
KEYRELEASE(input, idx, VK_CAPITAL);
KEYPRESS(input, idx, VK_CAPITAL);
break;
}
if (bKbState[VK_LSHIFT] & 0x80) {
// La touche Shift gauche était enfoncée
KEYPRESS(input, idx, VK_LSHIFT);
}
if (bKbState[VK_RSHIFT] & 0x80) {
// La touche Shift droit était enfoncée
KEYPRESS(input, idx, VK_RSHIFT);
}
if (bKbState[VK_LCONTROL] & 0x80) {
// La touche Ctrl gauche était enfoncée
KEYPRESS(input, idx, VK_LCONTROL);
}
if (bKbState[VK_RCONTROL] & 0x80) {
// La touche Ctrl droit était enfoncée
KEYPRESS(input, idx, VK_RCONTROL);
}
if (bKbState[VK_LMENU] & 0x80) {
// La touche Alt gauche était enfoncée
KEYPRESS(input, idx, VK_LMENU);
}
if (bKbState[VK_RMENU] & 0x80) {
// La touche Alt droit était enfoncée
KEYPRESS(input, idx, VK_RMENU);
}
SendInput(idx, input, sizeof INPUT);
return TRUE;
}

Quand la touche Shift est enfoncée, aucun caractère n'est envoyé. Et
quand le voyant Caps Lock est allumé, il y a quelques caractères qui
sont échangés (M->m, m->M, 8->_, _->8).
Avatar
Olivier Miakinen
Le 22/02/2008 14:57, j'écrivais :

[...] Par exemple,



Saleté d'interface chaise-clavier ! ;-)

Bon, je crois que le message reste compréhensible malgré cette phrase
non terminée.
Avatar
Christian ASTOR
On 22 fév, 14:57, Olivier Miakinen <om+ wrote:

Ça ne marche pas. :-(

Plus exactement, cette fonction fait bien ce qu'il faut quand aucune
touche n'est enfoncée et que le voyant Caps Lock est éteint, mais pas
dans le cas contraire.



Oui, il faut tester l'état du clavier avant, le modifier et le
remettre à l'état initial
Par ex, ceci marche chez moi (XP SP2) sur le Notepad et j'obtiens bien
dans tous les cas :
"Hello ! 12345&é'(~#{["
=>

BOOL bCtrl = GetKeyState(VK_CONTROL) & 0x8000;
BOOL bAlt = GetKeyState(VK_MENU) & 0x8000;
BOOL bShift = GetKeyState(VK_SHIFT) & 0x8000;
BOOL bCapsOn = GetKeyState(VK_CAPITAL) & 0x0001;

if (bCtrl)
GenerateKey(KEYEVENTF_KEYUP, 0, VK_CONTROL, 0);
if (bAlt)
GenerateKey(KEYEVENTF_KEYUP, 0, VK_MENU, 0);
if (bShift)
GenerateKey(KEYEVENTF_KEYUP, 0, VK_SHIFT, 0);
if (bCapsOn)
{
GenerateKey(0, 0, VK_CAPITAL, 0);
GenerateKey(KEYEVENTF_KEYUP, 0, VK_CAPITAL, 0);
}

GenerateText("Hello ! 12345&é'(~#{[");

if (bCtrl)
GenerateKey(0, 0, VK_CONTROL, 0);
if (bAlt)
GenerateKey(0, 0, VK_MENU, 0);
if (bShift)
GenerateKey(0, 0,VK_SHIFT, 0);
if (bCapsOn)
{
GenerateKey(0, 0, VK_CAPITAL, 0);
GenerateKey(KEYEVENTF_KEYUP, 0, VK_CAPITAL, 0);
}

avec

BOOL GenerateKey(int nEvent, int nType, WORD nCode, char cChar)
{
INPUT input={0};
if (nType)
{
input.ki.wVk = VkKeyScan (cChar);
input.ki.wScan = LOWORD(OemKeyScan (cChar));
}
else
{
input.ki.wVk = nCode;
input.ki.wScan = LOWORD(MapVirtualKey(nCode, 0));
}
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = nEvent;
return SendInput(1, &input, sizeof INPUT);
}
Avatar
Olivier Miakinen
Le 22/02/2008 17:13, Christian ASTOR a écrit :

Plus exactement, cette fonction fait bien ce qu'il faut quand aucune
touche n'est enfoncée et que le voyant Caps Lock est éteint, mais pas
dans le cas contraire.



Oui, il faut tester l'état du clavier avant, le modifier et le
remettre à l'état initial



C'est bien ce que je croyais avoir fait avec GetKeyboardState() et
SendInput() !

Par ex, ceci marche chez moi (XP SP2) sur le Notepad et j'obtiens bien
dans tous les cas :
"Hello ! 12345&é'(~#{["
=>

[...]



Encore merci, j'essaye ça tout de suite.
Avatar
Olivier Miakinen
Le 22/02/2008 17:20, je répondais à Christian ASTOR :

Encore merci, j'essaye ça tout de suite.



Malheureusement ça ne fonctionne toujours pas : aucun caractère n'est
reçu par Notepad quand la touche Shift est enfoncée. :-(

Je soupçonne un effet de bord dû à un bout de code ailleurs que là où je
suis en train de regarder... je vais essayer de mettre des traces un peu
partout ! En tout cas merci encore pour ton aide.