2016-06-19

Dica rápida: simulando teclas pressionadas


TL;DR: Se você considera a documentação da Microsoft confusa, enigmática, incompleta e perigosa, você não está sozinho. Use os códigos abaixo como um guia mas por sua conta e risco. E execute os aplicativos como Administrador ao clicar sobre eles ou como usuário restrito pelo CMD.

Escrito em C, utilizando SendInput: main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
 *  SendInput VKMP or VKBB keys
 *
 *  Please use: http://wmpkeys.sourceforge.net/ // Make "Fast Backward" use VK_BROWSER_BACK
 *
 *  @Author:  Fernando Di Ramos
 *     Here is where I hide: https://ofernandofilo.blogspot.com.br/
 *  @License: WTFPL Version 2
 *     SEE: https://en.wikipedia.org/wiki/WTFPL
 *
 *  This software was tested only with:
 *     Windows 7 Home Basic, Service Pack 1, 7601, x64, pt_BR [not free]
 *     Windows Media Player 12.0.7601.19148 [not free]
 *     WMP Keys 1.2.0.0 [free]
 */
#define WINVER 0x0601
// SEE: https://msdn.microsoft.com/en-us/library/aa383745%28VS.85%29.aspx
// SEE: https://msdn.microsoft.com/en-us/library/windows/hardware/ff554695%28v=vs.85%29.aspx
#define VK_BROWSER_BACK     0xA6
#define VK_MEDIA_PLAY_PAUSE 0xB3
// SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
#define WSCAN_BROWSER_BACK     0x6A
#define WSCAN_MEDIA_PLAY_PAUSE 0x22
// SEE: https://support.microsoft.com/en-us/kb/274333
// SEE: http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
#define KEYEVENTF_KEYDOWN   0x0000
#define KEYEVENTF_KEYUP     0x0002
// SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646271%28v=vs.85%29.aspx
// SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646280%28v=vs.85%29.aspx
// SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646281%28v=vs.85%29.aspx
#define nInputs           1 // # of inputs
#define INPUT_MOUSE       0
#define INPUT_KEYBOARD    1
#define INPUT_HARDWARE    2
// SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310%28v=vs.85%29.aspx

/**
 * Keyboard Input
 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx
 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms646268%28v=vs.85%29.aspx
 *
 * Scancodes Sets
 * https://en.wikipedia.org/wiki/Scancode#Scancode_sets
 * https://www.win.tue.nl/~aeb/linux/kbd/scancodes.html
 * https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html
 * http://www.quadibloc.com/comp/scan.htm
 *
 */

#include <windows.h>

int main() {
   /**
    * You must define a KEYBDINPUT
    * SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646271%28v=vs.85%29.aspx
    */
   KEYBDINPUT keybdinput;
   keybdinput.wVk = VK_MEDIA_PLAY_PAUSE;
   keybdinput.wScan = WSCAN_MEDIA_PLAY_PAUSE;
   keybdinput.dwFlags = KEYEVENTF_KEYDOWN;
   keybdinput.time = 0;
   keybdinput.dwExtraInfo = 0;

   /**
    * You must define a LPINPUT/INPUT, which uses KEYBDINPUT
    * SEE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646270%28v=vs.85%29.aspx
    */
   LPINPUT lpinput;
   INPUT input;
   input.type = INPUT_KEYBOARD;
   input.ki = keybdinput;
   lpinput = &input;

   /**
    * SendInput function
    * SEE:https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310%28v=vs.85%29.aspx
    */
   // SendInput(UINT nInputs,LPINPUT pInputs,int cbSize);
   // Press down
   // SendInput(nInputs,lpinput,sizeof(input));

   // Release key
   keybdinput.dwFlags = KEYEVENTF_KEYUP;
   SendInput(nInputs,lpinput,sizeof(input));

   return 0;
}

/**
 *  Still confuse?
 *     SEE: https://batchloaf.wordpress.com/2012/04/17/simulating-a-keystroke-in-win32-c-or-c-using-sendinput/
 *     SEE: https://batchloaf.wordpress.com/2012/10/18/simulating-a-ctrl-v-keystroke-in-win32-c-or-c-using-sendinput/
 */
* código convertido para html por: hilite.me, style friendly with line numbers.

Escrito em C, utilizando SendInput, sem comentários: main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#define WINVER 0x0601          // Windows 7++
#define VK_BROWSER_BACK        0xA6
#define VK_MEDIA_PLAY_PAUSE    0xB3
#define WSCAN_BROWSER_BACK     0x6A
#define WSCAN_MEDIA_PLAY_PAUSE 0x22
#define KEYEVENTF_KEYDOWN      0x0000
#define KEYEVENTF_KEYUP        0x0002
#define nInputs                1
#define INPUT_MOUSE            0
#define INPUT_KEYBOARD         1
#define INPUT_HARDWARE         2
#include <windows.h>

int main() {
   KEYBDINPUT keybdinput;
   keybdinput.wVk = VK_MEDIA_PLAY_PAUSE;
   keybdinput.wScan = WSCAN_MEDIA_PLAY_PAUSE;
   keybdinput.dwFlags = KEYEVENTF_KEYDOWN;
   keybdinput.time = 0;
   keybdinput.dwExtraInfo = 0;

   LPINPUT lpinput;
   INPUT input;
   input.type = INPUT_KEYBOARD;
   input.ki = keybdinput;
   lpinput = &input;

   // Release key
   keybdinput.dwFlags = KEYEVENTF_KEYUP;
   SendInput(nInputs,lpinput,sizeof(input));

   return 0;
}
* código convertido para html por: hilite.me, style friendly with line numbers.

Escrito em C, utilizando keybd_event, sem comentários: main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#define WINVER 0x0500       // Windows 2000++
#define VK_MEDIA_PLAY_PAUSE 0xB3
#include <windows.h>
int main () {
    // Simulate a key press
    keybd_event(VK_MEDIA_PLAY_PAUSE,0,0,0);

   // Simulate a key release
   keybd_event(VK_MEDIA_PLAY_PAUSE,0,1,0);

   return 0;
}
* código convertido para html por: hilite.me, style friendly with line numbers.

Escrito em JScript 5.7, utilizando SendKeys: sendkeystowmpkeys.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
 * WINDOWS SCRIPT JSCRIPT
 *
 * CALL ME:
 *   wscript.exe sendkeystowmpkeys.js
 *
 */
 
var sendtowindows = WScript.CreateObject("WScript.Shell");
// SEND: CTRL + ALT + HOME
sendtowindows.SendKeys("^%{HOME}");
* código convertido para html por: hilite.me, style friendly with line numbers.


Breve explicação: a documentação oficial da Microsoft recomenda o uso da função SendInput sobre o uso da função keybd_event. Ambas permitem utilizar teclas do mapa de teclas virtuais, Virtual-Key Codes, porém, são, como se vê, bem difíceis de lidar.

SendInput and keybd_envent only

A função SendKeys é bem mais simples de trabalhar, porém, só trabalha com um número limitado de teclas. Infelizmente, para simular teclas multimídia como VK_MEDIA_PLAY_PAUSE é preciso utilizar SendInput ou keybd_event.

SendInput, keybd_event and SendKeys

Todos os códigos presentes foram testados no Windows 7 Home Basic, SP1, 64 bits, pt_BR, utilizando Code::Blocks com compilador TDM-GCC (para instalação completa baixe o arquivo: codeblocks-16.01mingw-setup.exe) e utilizando a opção -static do linker. (Settings > Compiler... > Linker settings > Other linker options: -static). Também foi utilizado o Notepad++ para desenvolvimento em JScript.



Considerações finais: todos os códigos apresentados apenas simulam o pressionamento de teclas como se o usuário as estivessem apertando na janela ativa. É preciso, portanto, que o programa que se espera receber tais teclas esteja aberto e seja a janela ativa (selecionada) no momento da execução.

Para enviar eventos de pressionamento de teclas para aplicações em segundo plano (backgroud) são necessárias outras funções, as quais não conheço e possivelmente não serão tratadas aqui.

Estes códigos fazem parte de um projeto que desenvolvi no meu trabalho, estão em uso e apresentado aqui:


Imagens obtidas através do LightShot, Greenshot ou ShareX, todos gratuitos.


abraços!

Nenhum comentário:

Postar um comentário