I am trying to send simple key sequences (ctrl-p, shift-p) to the foreground app (which happens to be the Visual Studio editor unit test buffer). No matter what I try, I cannot get my test key sequences of control-p or shift-p to work properly. The 'p' appears in the buffer, but not with the control/shift part.
I have read easily a dozen code examples from SO and the net, read the documentation and tried variations too many to count. But without success. I'm on a standard Windows10x64 system, running the code as a unit test.
The code below sets both the virtual key and the scancode for the modifier keys, which is probably wrong in practice. I have tried both ways without success. I included the code to set both so that you could see the code I was using for both fields.
Does anyone know what I am doing wrong? Thank you.
** UPDATE ** My code was not failing. I was running it in a unit test within Visual Studio, which was blocking the modifier keys. When I ran the same code outside of a unit test (without all the debugging layers etc), the code ran fine.
Lesson: Don't try to debug your SendInput code modifier keys in unit tests!!
[DllImport("user32.dll", SetLastError = true)]
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, KeyInput[] pInputs, int cbSize);
[Flags]
public enum KeyEventFlag
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008
}
[Flags]
public enum InputType : int
{
Mouse = 0,
Keyboard = 1,
Hardware = 2
}
public struct KeyInput
{
public int type;
public WinSend.InputUnion u;
}
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion
{
[FieldOffset(0)] public MouseInput mi;
[FieldOffset(0)] public KeyboardInput ki;
[FieldOffset(0)] public HardwareInput hi;
}
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardInput
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
const uint MAPVK_VK_TO_VSC = 0x00;
const uint MAPVK_VSC_TO_VK = 0x01;
const uint MAPVK_VK_TO_CHAR = 0x02;
const uint MAPVK_VSC_TO_VK_EX = 0x03;
const uint MAPVK_VK_TO_VSC_EX = 0x04;
[TestMethod()]
public void SendControlKeyTest() {
DebugOn = true;
const uint VK_CONTROL = 0x11;
const uint VK_LCONTROL = 0xA2;
const uint VK_LSHIFT = 0xA0;
var keyList = new List<KeyInput>();
// wVk = A virtual-key code. The code must be a value in the range 1 to 254.
// If the dwFlags member specifies KEYEVENTF_UNICODE, wVk must be 0.
// wScan = A hardware scan code for the key. If dwFlags specifies KEYEVENTF_UNICODE,
// wScan specifies a Unicode char to send to the foreground application.
// the MapVirtualKey calls return successfully
var scanControl = MapVirtualKey((uint) VK_CONTROL, (uint) MAPVK_VK_TO_VSC);
if (scanControl == 0) Dprint("MapVirtualKey for VK_CONTROL failed.p");
var scanShift = MapVirtualKey((uint) VK_LSHIFT, (uint) MAPVK_VK_TO_VSC);
if (scanShift == 0) Dprint("MapVirtualKey for VK_LSHIFT failed.");
// this fails as is, fails with Flag.Unicode, and fails with no XXcode set
// this also fails with either VK_CONTROL or 0 in the wVk field
var vkdown = new KeyInput();
vkdown.type = (int) InputType.Keyboard;
vkdown.u.ki.wVk = (ushort) VK_CONTROL; // also fails with 0x0
vkdown.u.ki.dwFlags = (uint) (KeyEventFlag.Scancode | KeyEventFlag.KeyDown);
vkdown.u.ki.wScan = (ushort) scanControl;
keyList.Add(vkdown);
// this works - I see the 'p' when it runs
var keydown = new KeyInput();
keydown.type = (int) InputType.Keyboard;
keydown.u.ki.wVk = 0; // must be zero if Flag.Unicode is set
keydown.u.ki.dwFlags = (uint) (KeyEventFlag.Unicode | KeyEventFlag.KeyDown);
keydown.u.ki.wScan = (ushort) 'p';
keyList.Add(keydown);
// this works - I see the 'p' when it runs
var keyup = new KeyInput();
keyup.type = (int) InputType.Keyboard;
keyup.u.ki.wVk = 0; // must be zero if Flag.Unicode is set
keyup.u.ki.dwFlags = (uint) (KeyEventFlag.Unicode | KeyEventFlag.KeyUp);
keyup.u.ki.wScan = (ushort) 'p';
keyList.Add(keyup);
// this fails as is, fails with Flag.Unicode, and fails with no XXcode set
// this also fails with either VK_CONTROL or 0 in the wVk field
var vkup = new KeyInput();
vkup.type = (int) InputType.Keyboard;
vkup.u.ki.wVk = (ushort) VK_CONTROL; // also fails with 0x0
vkup.u.ki.dwFlags = (uint) (KeyEventFlag.Scancode| KeyEventFlag.KeyUp);
vkup.u.ki.wScan = (ushort) scanControl;
keyList.Add(vkup);
// SendInput returns 4, which means it sent 4 events successfully
var keycount = SendInput((uint) keyList.Count, keyList.ToArray(),
Marshal.SizeOf(typeof(KeyInput)));
Dprint($"Sent {keycount} keys to the system.");
// The Control-P should move my caret up one line, but it does not.
// Shift should create a capital P in the buffer, but it does not.
// All I see is a 'p' in the Visual studio buffer
}
Sorry for indirect answer but here's 100% working and tested solution for WPF application.
KeyInterop.VirtualKeyFromKey
here is standard method from .NET class. I guess that it can be replaced with WinAPIMapVirtualKey
.Usage