I want to send some keys to a game by SendInput API on C#. (My environment is Windows 7 64bit)
I wrote some codes as follows:
[DllImport("user32.dll")]
static extern int SendInput(int nInputs, INPUT[] pInputs, int cbSize);
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public short wVk;
public short wScan;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
public static void WalkFront()
{
INPUT[] inputs = new INPUT[1];
Keys virtualKeycode = (Keys)0x15;
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = (short)virtualKeycode;
inputs[0].ki.wScan = (short)MapVirtualKey((int)virtualKeycode, 0);
inputs[0].ki.dwFlags = KEYEVENTF_SCANCODE;
inputs[0].ki.time = 0;
inputs[0].ki.dwExtraInfo = GetMessageExtraInfo();
SendInput(1, inputs, Marshal.SizeOf(inputs[0]));
}
However it does not work correctly.
Apparently, this code can send BackSpace to the game. In fact, this code can delete a character on Notepad.exe.
Bizarrely, this code only sends BackSpace to the applications. I changed the keycode (I tried 0x14, 0x13, 0x12...etc) in this code. However, this code only sends BackSpace
I want to send another key (J, K, L...etc) to applications but I could not.
Any advices?
Thanks
Remember that a keypress event consists of a key-down and after that a key-up event. That means to "send a backspace" you need to call SendInput twice, once with the key pressed, once with the key released.
If you fail to call SendInput with the key released, subsequent key presses will most likely be ignored by most apps.
Related
I'm writing a helper for injecting touch in UI-tests using InjectTouchInput.
The injected touch works fine but injecting mouse input after touch does not work.
Mouse.Click(point); // works
Touch.Tap(point); // works
Mouse.Click(point); // does not work, mouse cursor no longer visible on screen.
Calling GetCursorInfo() reveals that cursor is CURSOR_SUPPRESSED and I have not found a way to restore it.
Moving the physical mouse brings back the cursor and clicking things work fine.
How can I restore things so that mouse works again here?
Use the API SendInput to Simulated mouse input.
PInvoke to SendInput – this is the official way to simulate input. It
pushes the input through all of the expected code paths, and is
indistinguishable from real input.
Here is the code sample:
public class MouseSimulator
{
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
public SendInputEventType type;
public MouseKeybdhardwareInputUnion mkhi;
}
[StructLayout(LayoutKind.Explicit)]
struct MouseKeybdhardwareInputUnion
{
[FieldOffset(0)]
public MouseInputData mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
}
struct MouseInputData
{
public int dx;
public int dy;
public uint mouseData;
public MouseEventFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[Flags]
enum MouseEventFlags : uint
{
MOUSEEVENTF_MOVE = 0x0001,
MOUSEEVENTF_LEFTDOWN = 0x0002,
MOUSEEVENTF_LEFTUP = 0x0004,
MOUSEEVENTF_RIGHTDOWN = 0x0008,
MOUSEEVENTF_RIGHTUP = 0x0010,
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
MOUSEEVENTF_MIDDLEUP = 0x0040,
MOUSEEVENTF_XDOWN = 0x0080,
MOUSEEVENTF_XUP = 0x0100,
MOUSEEVENTF_WHEEL = 0x0800,
MOUSEEVENTF_VIRTUALDESK = 0x4000,
MOUSEEVENTF_ABSOLUTE = 0x8000
}
enum SendInputEventType : int
{
InputMouse,
InputKeyboard,
InputHardware
}
public static void MoveMouseButton(int x, int y)
{
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = SendInputEventType.InputMouse;
mouseMoveInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_MOVE| MouseEventFlags.MOUSEEVENTF_ABSOLUTE;
mouseMoveInput.mkhi.mi.dx = x;
mouseMoveInput.mkhi.mi.dy = y;
SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT()));
}
}
I have developed a simple Console application to poll an Xbox Controller using xinput. I would like to use the values obtained from one of the thumbsticks to move the mouse. I am able to get the x and y values from the thumbstick, but when I use those values to SendInput() (using the User32.dll), the mouse does not move and the return value is 0.
According to Microsoft, "If the function returns zero, the input was already blocked by another thread."
How do I find the other thread that is blocking it? It is just a simple Console Application (exe) started by Visual Studio that prints the x and y values to the screen and attempts to move the mouse.
long x = controller.x; // values from the controller
long y = controller.y; // these are checked and do contain numbers
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = 0; // mouse
mouseMoveInput.mi.dx = x;
mouseMoveInput.mi.dy = y;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, ref mouseMoveInput, Marshal.SizeOf(new INPUT());
// result always returns 0
Am I missing something? Should this work?
Here are declarations:
[StructLayout(LayoutKind.Explicit)]
public struct MOUSEINPUT
{
[FieldOffset(0)]
public long X;
[FieldOffset(8)]
public long Y;
[FieldOffset(16)]
public uint MouseData;
[FieldOffset(20)]
public uint Flags;
[FieldOffset(24)]
public uint Time;
[FieldOffset(28)]
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBOARDINPUT
{
public ushort Vk;
public ushort Scan;
public uint Flags;
public uint Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBOARDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
UPDATE: using mouse-event does work, but this function is deprecated. Is there a problem with using it anyway since it works?
There is something odd I'm getting with the struct sizes:
Size of tagINPUT: 40
Size of mouseMoveInput: 40
Size of MOUSEINPUT: 32
Size of uint: 4
But if tagINPUT consists of MOUSEINPUT and uint then shouldn't it's size be 36?
The 2nd parameter of SendInput should be a pointer to an array, not a ref parameter, and especially not a ref directly to the struct.
I would also use explicit layout only for the struct that actually needs it, and let the rest be sequential. It's easier.
This code works for me:
const int INPUT_MOUSE = 0;
const int MOUSEEVENTF_MOVE = 0x0001;
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] inputs, int sizeOfInputStructure);
void Main()
{
INPUT mouseMoveInput = new INPUT();
mouseMoveInput.type = INPUT_MOUSE;
mouseMoveInput.mi.dx = 10;
mouseMoveInput.mi.dy = 10;
mouseMoveInput.mi.mouseData = 0;
mouseMoveInput.mi.dwFlags = MOUSEEVENTF_MOVE;
var result = SendInput(1, new INPUT[] { mouseMoveInput}, Marshal.SizeOf(mouseMoveInput));
if(result == 0) {
throw new Win32Exception();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint Msg;
public ushort ParamL;
public ushort ParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public uint type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
I'm using a Winform to give Buttons in a DirectX Game. Therefore I'm using this class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace DirectInput
{
public class cDirectInput
{
[DllImport("user32.dll")]
static extern UInt32 SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, Int32 cbSize);
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
public short wVk;
public short wScan;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
}
[StructLayout(LayoutKind.Explicit)]
struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
}
const int KEYEVENTF_EXTENDEDKEY = 0x0001;
const int KEYEVENTF_KEYUP = 0x0002;
const int KEYEVENTF_UNICODE = 0x0004;
const int KEYEVENTF_SCANCODE = 0x0008;
public void Send_Key(short Keycode, int KeyUporDown)
{
INPUT[] InputData = new INPUT[1];
InputData[0].type = 1;
InputData[0].ki.wScan = Keycode;
InputData[0].ki.dwFlags = KeyUporDown;
InputData[0].ki.time = 0;
InputData[0].ki.dwExtraInfo = IntPtr.Zero;
SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT)));
}
}
}
Then I'm sending Buttons with this:
DirectInput.cDirectInput d = new DirectInput.cDirectInput();
d.Send_Key(0x11, 0x0008);
But when I send it it is permanent. When I send w the player is moving forward forever. I can stop that by pressing w on the keyboard. Why isn't it stopping? When I send it to an editor it stops when I stop sending, ingame it doesn't stop. What is the problem?
Thank you in advance!
You're sending a key down message, but not sending a key up message following it. From the documentation:
KEYBDINPUT.dwFlags:
KEYEVENTF_KEYUP (0x0002)
If specified, the key is being released. If not specified, the key is being pressed.
To move forward for 1 second, try:
d.Send_Key(0x11, 0x0008);
Thread.Sleep(1000);
d.Send_Key(0x11, 0x000A);
Note that the reason pressing w fixes the problem is it sends a keydown event (which would be ignored) and then a keyup event, finally releasing the key.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
SendInput and 64bits
I'm using SendInput from .NET code (PInvoke). code used to work fine on 32bit OS, but now on WIN7 SendInput returns 0, and last error is set to 57 (ERROR_INVALID_PARAMETER).I cannot compile my code as x86 as I am loaded into a 64bit host. Also, I tried various solutions regarding structure sizes and field offsets, none worked.
These are my PInvoke imports and types:
[StructLayout(LayoutKind.Sequential)]
struct KEYBOARD_INPUT
{
public uint type;
public ushort vk;
public ushort scanCode;
public uint flags;
public uint time;
public uint extrainfo;
public uint padding1;
public uint padding2;
}
[DllImport("User32.dll", SetLastError=true)]
private static extern uint SendInput(
uint numberOfInputs,
[MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] KEYBOARD_INPUT[] input,
int structSize);
and code usage is:
uint result = SendInput(
(uint)inputs.Count,
inputs.ToArray(),
Marshal.SizeOf(inputs[0]));
where inputs array contains 1 KEYBOARD_INPUT struct.
this yields in result = 0, and when I check last error I get that last error is set to 57 (ERROR_INVALID_PARAMETER, The parameter is incorrect).
Is there a way to make this work under 64bit host in WIN7 64bit OS? this works in XP...
thanks
Try using the following definitions (courtesy of pinvoke.net):
const int INPUT_MOUSE = 0;
const int INPUT_KEYBOARD = 1;
const int INPUT_HARDWARE = 2;
const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
const uint KEYEVENTF_KEYUP = 0x0002;
const uint KEYEVENTF_UNICODE = 0x0004;
const uint KEYEVENTF_SCANCODE = 0x0008;
struct INPUT
{
public int type;
public InputUnion u;
}
[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
[FieldOffset(0)]
public MOUSEINPUT mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
/*Virtual Key code. Must be from 1-254. If the dwFlags member specifies KEYEVENTF_UNICODE, wVk must be 0.*/
public ushort wVk;
/*A hardware scan code for the key. If dwFlags specifies KEYEVENTF_UNICODE, wScan specifies a Unicode character which is to be sent to the foreground application.*/
public ushort wScan;
/*Specifies various aspects of a keystroke. See the KEYEVENTF_ constants for more information.*/
public uint dwFlags;
/*The time stamp for the event, in milliseconds. If this parameter is zero, the system will provide its own time stamp.*/
public uint time;
/*An additional value associated with the keystroke. Use the GetMessageExtraInfo function to obtain this information.*/
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
[DllImport("user32.dll")]
static extern IntPtr GetMessageExtraInfo();
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
Then, in your client code, use:
INPUT[] inputs = new INPUT[]
{
new INPUT
{
type = INPUT_KEYBOARD,
u = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = key,
wScan = 0,
dwFlags = 0,
dwExtraInfo = GetMessageExtraInfo(),
}
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
I'm working on a program to trigger cut and pastes
Pastes I have no problem with (I just dump a string into the clipboard)
Cut and or Copys are proving to be a little more difficult
The program I have is out of focus and has several hot keys registered with the os CTRL+ALT+2 CTRL+ALT+3 etc)
That I want to use to trigger Windows to copy anything that is highlighted in the window that is focused
I tried doing a sendkeys
SendKeys.Send("^c");
but that seems to work once or twice if at all then stop working.
is there a better way to try to trigger windows into coping highlighted content on a different window
One way to do this is by using the Win32 SendInput function. With SendInput, you have to simulate both the key down and key up events in order for the full key press to register. To simulate CTRL+C, you'd have to do:
CTRL key down
C key down
C key up
CTRL key up
pinvoke.net has some examples of SendInput usage. One issue to be mindful of is if the key is already pressed. You can use GetAsyncKeyState to only simulate a key down event if the key is not already down.
Below is some example code of how you could simulate CTRL+C. With the code below, you can simply call Keyboard.SimulateKeyStroke('c', ctrl: true); Note that this works as if the user literally pressed CTRL+C, so the active application will behave as it always does when such an event happens (i.e. if nothing is normally copied, then nothing will be copied with this method, either).
Edit: See David's comment below about batching the sent input. The code below should be sending the entire sequence of input events through a single call to SendInput to avoid being interleaved (and misinterpreted) with real user input events.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace SimulateKeys
{
static class Keyboard
{
public static void SimulateKeyStroke(char key, bool ctrl = false, bool alt = false, bool shift = false)
{
List<ushort> keys = new List<ushort>();
if (ctrl)
keys.Add(VK_CONTROL);
if (alt)
keys.Add(VK_MENU);
if (shift)
keys.Add(VK_SHIFT);
keys.Add(char.ToUpper(key));
INPUT input = new INPUT();
input.type = INPUT_KEYBOARD;
int inputSize = Marshal.SizeOf(input);
for (int i = 0; i < keys.Count; ++i)
{
input.mkhi.ki.wVk = keys[i];
bool isKeyDown = (GetAsyncKeyState(keys[i]) & 0x10000) != 0;
if (!isKeyDown)
SendInput(1, ref input, inputSize);
}
input.mkhi.ki.dwFlags = KEYEVENTF_KEYUP;
for (int i = keys.Count - 1; i >= 0; --i)
{
input.mkhi.ki.wVk = keys[i];
SendInput(1, ref input, inputSize);
}
}
[DllImport("user32.dll")]
static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);
[DllImport("user32.dll")]
static extern short GetAsyncKeyState(ushort vKey);
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
}
[StructLayout(LayoutKind.Explicit)]
struct MOUSEKEYBDHARDWAREINPUT
{
[FieldOffset(0)]
public MOUSEINPUT mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
struct INPUT
{
public int type;
public MOUSEKEYBDHARDWAREINPUT mkhi;
}
const int INPUT_KEYBOARD = 1;
const uint KEYEVENTF_KEYUP = 0x0002;
const ushort VK_SHIFT = 0x10;
const ushort VK_CONTROL = 0x11;
const ushort VK_MENU = 0x12;
}
class Program
{
static void Main(string[] args)
{
Thread.Sleep(3000);
Keyboard.SimulateKeyStroke('c', ctrl: true);
}
}
}
If you can get the selected text from the focused window (maybe an easier problem to solve) then you're better off using the SetText method of the System.Windows.Forms.Clipboard class.