J'utilise ce tableau comme clavier à des fins de démonstration.
Quoi qu'il en soit, pour faire la longue histoire, tout fonctionne bien, sauf dans de très rares cas. J'envoie des frappes avec la fonction SendInput située dans user32.dll.
Donc, mon programme ressemble à:
static void Main(string[] args)
{
Console.Write("Press enter an on the next secont the key combination shift+end will be send");
Console.Read();
Thread.Sleep(1000);
SendKeyDown(KeyCode.SHIFT);
SendKeyPress(KeyCode.END);
SendKeyUp(KeyCode.SHIFT);
Console.Read();
Console.Read();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputStructure);
/// <summary>
/// simulate key press
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyPress(KeyCode keyCode)
{
INPUT input = new INPUT {
Type = 1
};
input.Data.Keyboard = new KEYBDINPUT() {
Vk = (ushort)keyCode,
Scan = 0,
Flags = 0,
Time = 0,
ExtraInfo = IntPtr.Zero,
};
INPUT input2 = new INPUT {
Type = 1
};
input2.Data.Keyboard = new KEYBDINPUT() {
Vk = (ushort)keyCode,
Scan = 0,
Flags = 2,
Time = 0,
ExtraInfo = IntPtr.Zero
};
INPUT[] inputs = new INPUT[] { input, input2 };
if (SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
throw new Exception();
}
/// <summary>
/// Send a key down and hold it down until sendkeyup method is called
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyDown(KeyCode keyCode)
{
INPUT input = new INPUT{
Type = 1
};
input.Data.Keyboard = new KEYBDINPUT();
input.Data.Keyboard.Vk = (ushort)keyCode;
input.Data.Keyboard.Scan = 0;
input.Data.Keyboard.Flags = 0;
input.Data.Keyboard.Time = 0;
input.Data.Keyboard.ExtraInfo = IntPtr.Zero;
INPUT[] inputs = new INPUT[] { input };
if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
{
throw new Exception();
}
}
/// <summary>
/// Release a key that is being hold down
/// </summary>
/// <param name="keyCode"></param>
public static void SendKeyUp(KeyCode keyCode)
{
INPUT input = new INPUT {
Type = 1
};
input.Data.Keyboard = new KEYBDINPUT();
input.Data.Keyboard.Vk = (ushort)keyCode;
input.Data.Keyboard.Scan = 0;
input.Data.Keyboard.Flags = 2;
input.Data.Keyboard.Time = 0;
input.Data.Keyboard.ExtraInfo = IntPtr.Zero;
INPUT[] inputs = new INPUT[] { input };
if (SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))) == 0)
throw new Exception();
}
Et voici les structures que j'ai trouvées en ligne que ces méthodes utilisent ainsi que les codes de clé: (notez que cela ressemble à beaucoup de code et c'est parce qu'il y a beaucoup de codes de clé dans un Enum)
/// <summary>
/// http://msdn.Microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct INPUT
{
public uint Type;
public MOUSEKEYBDHARDWAREINPUT Data;
}
/// <summary>
/// http://social.msdn.Microsoft.com/Forums/en/csharplanguage/thread/f0e82d6e-4999-4d22-b3d3-32b25f61fb2a
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct MOUSEKEYBDHARDWAREINPUT
{
[FieldOffset(0)]
public HARDWAREINPUT Hardware;
[FieldOffset(0)]
public KEYBDINPUT Keyboard;
[FieldOffset(0)]
public MOUSEINPUT Mouse;
}
/// <summary>
/// http://msdn.Microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
/// <summary>
/// http://msdn.Microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
public ushort Vk;
public ushort Scan;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
/// <summary>
/// http://social.msdn.Microsoft.com/forums/en-US/netfxbcl/thread/2abc6be8-c593-4686-93d2-89785232dacd
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
public int X;
public int Y;
public uint MouseData;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
public enum KeyCode : ushort
{
#region Media
/// <summary>
/// Next track if a song is playing
/// </summary>
MEDIA_NEXT_TRACK = 0xb0,
/// <summary>
/// Play pause
/// </summary>
MEDIA_PLAY_PAUSE = 0xb3,
/// <summary>
/// Previous track
/// </summary>
MEDIA_PREV_TRACK = 0xb1,
/// <summary>
/// Stop
/// </summary>
MEDIA_STOP = 0xb2,
#endregion
#region math
/// <summary>Key "+"</summary>
ADD = 0x6b,
/// <summary>
/// "*" key
/// </summary>
MULTIPLY = 0x6a,
/// <summary>
/// "/" key
/// </summary>
DIVIDE = 0x6f,
/// <summary>
/// Subtract key "-"
/// </summary>
SUBTRACT = 0x6d,
#endregion
#region Browser
/// <summary>
/// Go Back
/// </summary>
BROWSER_BACK = 0xa6,
/// <summary>
/// Favorites
/// </summary>
BROWSER_FAVORITES = 0xab,
/// <summary>
/// Forward
/// </summary>
BROWSER_FORWARD = 0xa7,
/// <summary>
/// Home
/// </summary>
BROWSER_HOME = 0xac,
/// <summary>
/// Refresh
/// </summary>
BROWSER_REFRESH = 0xa8,
/// <summary>
/// browser search
/// </summary>
BROWSER_SEARCH = 170,
/// <summary>
/// Stop
/// </summary>
BROWSER_STOP = 0xa9,
#endregion
#region Numpad numbers
/// <summary>
///
/// </summary>
NUMPAD0 = 0x60,
/// <summary>
///
/// </summary>
NUMPAD1 = 0x61,
/// <summary>
///
/// </summary>
NUMPAD2 = 0x62,
/// <summary>
///
/// </summary>
NUMPAD3 = 0x63,
/// <summary>
///
/// </summary>
NUMPAD4 = 100,
/// <summary>
///
/// </summary>
NUMPAD5 = 0x65,
/// <summary>
///
/// </summary>
NUMPAD6 = 0x66,
/// <summary>
///
/// </summary>
NUMPAD7 = 0x67,
/// <summary>
///
/// </summary>
NUMPAD8 = 0x68,
/// <summary>
///
/// </summary>
NUMPAD9 = 0x69,
#endregion
#region Fkeys
/// <summary>
/// F1
/// </summary>
F1 = 0x70,
/// <summary>
/// F10
/// </summary>
F10 = 0x79,
/// <summary>
///
/// </summary>
F11 = 0x7a,
/// <summary>
///
/// </summary>
F12 = 0x7b,
/// <summary>
///
/// </summary>
F13 = 0x7c,
/// <summary>
///
/// </summary>
F14 = 0x7d,
/// <summary>
///
/// </summary>
F15 = 0x7e,
/// <summary>
///
/// </summary>
F16 = 0x7f,
/// <summary>
///
/// </summary>
F17 = 0x80,
/// <summary>
///
/// </summary>
F18 = 0x81,
/// <summary>
///
/// </summary>
F19 = 130,
/// <summary>
///
/// </summary>
F2 = 0x71,
/// <summary>
///
/// </summary>
F20 = 0x83,
/// <summary>
///
/// </summary>
F21 = 0x84,
/// <summary>
///
/// </summary>
F22 = 0x85,
/// <summary>
///
/// </summary>
F23 = 0x86,
/// <summary>
///
/// </summary>
F24 = 0x87,
/// <summary>
///
/// </summary>
F3 = 0x72,
/// <summary>
///
/// </summary>
F4 = 0x73,
/// <summary>
///
/// </summary>
F5 = 0x74,
/// <summary>
///
/// </summary>
F6 = 0x75,
/// <summary>
///
/// </summary>
F7 = 0x76,
/// <summary>
///
/// </summary>
F8 = 0x77,
/// <summary>
///
/// </summary>
F9 = 120,
#endregion
#region Other
/// <summary>
///
/// </summary>
OEM_1 = 0xba,
/// <summary>
///
/// </summary>
OEM_102 = 0xe2,
/// <summary>
///
/// </summary>
OEM_2 = 0xbf,
/// <summary>
///
/// </summary>
OEM_3 = 0xc0,
/// <summary>
///
/// </summary>
OEM_4 = 0xdb,
/// <summary>
///
/// </summary>
OEM_5 = 220,
/// <summary>
///
/// </summary>
OEM_6 = 0xdd,
/// <summary>
///
/// </summary>
OEM_7 = 0xde,
/// <summary>
///
/// </summary>
OEM_8 = 0xdf,
/// <summary>
///
/// </summary>
OEM_CLEAR = 0xfe,
/// <summary>
///
/// </summary>
OEM_COMMA = 0xbc,
/// <summary>
///
/// </summary>
OEM_MINUS = 0xbd,
/// <summary>
///
/// </summary>
OEM_PERIOD = 190,
/// <summary>
///
/// </summary>
OEM_PLUS = 0xbb,
#endregion
#region KEYS
/// <summary>
///
/// </summary>
KEY_0 = 0x30,
/// <summary>
///
/// </summary>
KEY_1 = 0x31,
/// <summary>
///
/// </summary>
KEY_2 = 50,
/// <summary>
///
/// </summary>
KEY_3 = 0x33,
/// <summary>
///
/// </summary>
KEY_4 = 0x34,
/// <summary>
///
/// </summary>
KEY_5 = 0x35,
/// <summary>
///
/// </summary>
KEY_6 = 0x36,
/// <summary>
///
/// </summary>
KEY_7 = 0x37,
/// <summary>
///
/// </summary>
KEY_8 = 0x38,
/// <summary>
///
/// </summary>
KEY_9 = 0x39,
/// <summary>
///
/// </summary>
KEY_A = 0x41,
/// <summary>
///
/// </summary>
KEY_B = 0x42,
/// <summary>
///
/// </summary>
KEY_C = 0x43,
/// <summary>
///
/// </summary>
KEY_D = 0x44,
/// <summary>
///
/// </summary>
KEY_E = 0x45,
/// <summary>
///
/// </summary>
KEY_F = 70,
/// <summary>
///
/// </summary>
KEY_G = 0x47,
/// <summary>
///
/// </summary>
KEY_H = 0x48,
/// <summary>
///
/// </summary>
KEY_I = 0x49,
/// <summary>
///
/// </summary>
KEY_J = 0x4a,
/// <summary>
///
/// </summary>
KEY_K = 0x4b,
/// <summary>
///
/// </summary>
KEY_L = 0x4c,
/// <summary>
///
/// </summary>
KEY_M = 0x4d,
/// <summary>
///
/// </summary>
KEY_N = 0x4e,
/// <summary>
///
/// </summary>
KEY_O = 0x4f,
/// <summary>
///
/// </summary>
KEY_P = 80,
/// <summary>
///
/// </summary>
KEY_Q = 0x51,
/// <summary>
///
/// </summary>
KEY_R = 0x52,
/// <summary>
///
/// </summary>
KEY_S = 0x53,
/// <summary>
///
/// </summary>
KEY_T = 0x54,
/// <summary>
///
/// </summary>
KEY_U = 0x55,
/// <summary>
///
/// </summary>
KEY_V = 0x56,
/// <summary>
///
/// </summary>
KEY_W = 0x57,
/// <summary>
///
/// </summary>
KEY_X = 0x58,
/// <summary>
///
/// </summary>
KEY_Y = 0x59,
/// <summary>
///
/// </summary>
KEY_Z = 90,
#endregion
#region volume
/// <summary>
/// Decrese volume
/// </summary>
VOLUME_DOWN = 0xae,
/// <summary>
/// Mute volume
/// </summary>
VOLUME_MUTE = 0xad,
/// <summary>
/// Increase volue
/// </summary>
VOLUME_UP = 0xaf,
#endregion
/// <summary>
/// Take snapshot of the screen and place it on the clipboard
/// </summary>
SNAPSHOT = 0x2c,
/// <summary>Send right click from keyboard "key that is 2 keys to the right of space bar"</summary>
RightClick = 0x5d,
/// <summary>
/// Go Back or delete
/// </summary>
BACKSPACE = 8,
/// <summary>
/// Control + Break "When debuging if you step into an infinite loop this will stop debug"
/// </summary>
CANCEL = 3,
/// <summary>
/// Caps lock key to send cappital letters
/// </summary>
CAPS_LOCK = 20,
/// <summary>
/// Ctlr key
/// </summary>
CONTROL = 0x11,
/// <summary>
/// Alt key
/// </summary>
ALT = 18,
/// <summary>
/// "." key
/// </summary>
DECIMAL = 110,
/// <summary>
/// Delete Key
/// </summary>
DELETE = 0x2e,
/// <summary>
/// Arrow down key
/// </summary>
DOWN = 40,
/// <summary>
/// End key
/// </summary>
END = 0x23,
/// <summary>
/// Escape key
/// </summary>
ESC = 0x1b,
/// <summary>
/// Home key
/// </summary>
HOME = 0x24,
/// <summary>
/// Insert key
/// </summary>
INSERT = 0x2d,
/// <summary>
/// Open my computer
/// </summary>
LAUNCH_APP1 = 0xb6,
/// <summary>
/// Open calculator
/// </summary>
LAUNCH_APP2 = 0xb7,
/// <summary>
/// Open default email in my case Outlook
/// </summary>
LAUNCH_MAIL = 180,
/// <summary>
/// Opend default media player (iTunes, winmediaplayer, etc)
/// </summary>
LAUNCH_MEDIA_SELECT = 0xb5,
/// <summary>
/// Left control
/// </summary>
LCONTROL = 0xa2,
/// <summary>
/// Left arrow
/// </summary>
LEFT = 0x25,
/// <summary>
/// Left shift
/// </summary>
LSHIFT = 160,
/// <summary>
/// left windows key
/// </summary>
LWIN = 0x5b,
/// <summary>
/// Next "page down"
/// </summary>
PAGEDOWN = 0x22,
/// <summary>
/// Num lock to enable typing numbers
/// </summary>
NUMLOCK = 0x90,
/// <summary>
/// Page up key
/// </summary>
PAGE_UP = 0x21,
/// <summary>
/// Right control
/// </summary>
RCONTROL = 0xa3,
/// <summary>
/// Return key
/// </summary>
ENTER = 13,
/// <summary>
/// Right arrow key
/// </summary>
RIGHT = 0x27,
/// <summary>
/// Right shift
/// </summary>
RSHIFT = 0xa1,
/// <summary>
/// Right windows key
/// </summary>
RWIN = 0x5c,
/// <summary>
/// Shift key
/// </summary>
SHIFT = 0x10,
/// <summary>
/// Space back key
/// </summary>
SPACE_BAR = 0x20,
/// <summary>
/// Tab key
/// </summary>
TAB = 9,
/// <summary>
/// Up arrow key
/// </summary>
UP = 0x26,
}
Alors maintenant, ma question est pourquoi, quand j'envoie cette combinaison de touches, je n'obtiens pas les mêmes résultats que lorsque je le fais sur un vrai clavier? 98% des choses fonctionnent. Par exemple je suis capable de faire:
SendKeyDown(KeyCode.SHIFT);
SendKeyPress(KeyCode.KEY_A );
SendKeyUp(KeyCode.SHIFT);
Et cela enverra un A. capital
Devrais-je utiliser une autre bibliothèque?
La raison pour laquelle j'apprécie cette approche est que je ne sais pas à l'avance si l'utilisateur enverra une combinaison de touches Par exemple, dans les formulaires Windows Si je le fais:
System.Windows.Forms.SendKeys.SendWait("+{end}");
qui enverra shift + end mais peut-être que l'utilisateur veut juste envoyer shift ...
Vous ne définissez pas les indicateurs ni les champs d'analyse. En fonction de la frappe souhaitée, vous devrez les définir correctement pour que le système d'exploitation reconnaisse correctement les touches.
Vous pouvez envisager d’utiliser la bibliothèque Input Simulator , car elle fait déjà ce que vous voulez et vous n’avez pas à recréer la roue. Assurez-vous simplement de regarder à travers les forums car il y a quelques bonnes corrections à installer, car l'auteur a abandonné le projet en 2009. C'est quand même une bonne bibliothèque.
Une autre façon d'envoyer une entrée au clavier dans une fenêtre (que j'utilise pour les tests d'interface utilisateur) consiste à utiliser l'alternative Unicode dans KEYBDINPUT qui vous évite de mapper chaque caractère sur la clé virtuelle:
public static void SendString(string inputStr)
{
var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
WinAPI.SetForegroundWindow(hWnd);
List<WinAPI.INPUT> keyList = new List<WinAPI.INPUT>();
foreach (short c in inputStr)
{
switch (c)
{
case 8: // Translate \t to VK_TAB
{
WinAPI.INPUT keyDown = new WinAPI.INPUT();
keyDown.type = 1; //Keyboard
keyDown.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_TAB;
keyDown.union.keyboardInput.dwFlags = 0;
keyDown.union.keyboardInput.wScan = 0; //use VirtualKey
keyList.Add(keyDown);
WinAPI.INPUT keyUp = new WinAPI.INPUT();
keyUp.type = 1; //Keyboard
keyUp.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_TAB;
keyUp.union.keyboardInput.dwFlags = 0x0002;
keyUp.union.keyboardInput.wScan = 0; //use VirtualKey
keyList.Add(keyUp);
}
break;
case 10: // Translate \n to VK_RETURN
{
WinAPI.INPUT keyDown = new WinAPI.INPUT();
keyDown.type = 1; //Keyboard
keyDown.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_RETURN;
keyDown.union.keyboardInput.dwFlags = 0;
keyDown.union.keyboardInput.wScan = 0; //use VirtualKey
keyList.Add(keyDown);
WinAPI.INPUT keyUp = new WinAPI.INPUT();
keyUp.type = 1; //Keyboard
keyUp.union.keyboardInput.wVk = (short)WinAPI.WindowsVirtualKey.VK_RETURN;
keyUp.union.keyboardInput.dwFlags = 0x0002;
keyUp.union.keyboardInput.wScan = 0; //use VirtualKey
keyList.Add(keyUp);
}
break;
default:
{
WinAPI.INPUT keyDown = new WinAPI.INPUT();
keyDown.type = 1; //Keyboard
keyDown.union.keyboardInput.wVk = 0; //Use unicode
keyDown.union.keyboardInput.dwFlags = 0x0004; //Unicode Key Down
keyDown.union.keyboardInput.wScan = c;
keyList.Add(keyDown);
WinAPI.INPUT keyUp = new WinAPI.INPUT();
keyUp.type = 1; //Keyboard
keyUp.union.keyboardInput.wVk = 0; //Use unicode
keyUp.union.keyboardInput.dwFlags = 0x0004 | 0x0002; //Unicode Key Up
keyUp.union.keyboardInput.wScan = c;
keyList.Add(keyUp);
}
break;
}
}
WinAPI.SendInput((uint)keyList.Count, keyList.ToArray(), Marshal.SizeOf(typeof(WinAPI.INPUT)));
}