How to Send Inputs using C#






4.97/5 (29 votes)
A guide on how to use the Windows API and C# to send Inputs
Using the Code
We will be using the SendInput function in user32.dll. We will not use keybd_event and mouse_event as they are outdated, which means they might not work correctly in future versions of Windows.
DirectInput
is an API for collecting user input from a user, via input devices (keyboard, mouse, etc.). Depending on some flags that we will discuss later, we can send virtual scan codes or hardware scan codes. Virtual scan codes might be ignored by DirectInput
, meaning that our inputs will not be executed. Hardware scan codes are more like pressing a key manually.
The SendInput
function takes three parameters, the number of inputs, an array of INPUT for the inputs we want to send, and the size of our INPUT
struct. The INPUT
struct includes an integer that indicates the type of input and a union for the inputs that will be passed. If you want to read more about unions, check out this wiki.
First, let's implement the input structs KEYBDINPUT, MOUSEINPUT, and HARDWAREINPUT.
Input Structs
KEYBDINPUT Struct
We will use Sequential StructLayout
to force the members to be in sequential order because we will pass the struct
to unmanaged code. wVk
is a virtual key code. wScan
is the scan code of the key we want to press. More details about the scan codes can be found here. dwFlags
are the flags about the input (KeyUp
, ExtendKey
, Unicode
, ScanCode
). Please read the remarks section here for more information about the flags. We will see them in action later. time
is a timestamp of the input, if it is set to 0
, then the system provides its own timestamp. dwExtraInfo
provides additional information about the keystroke, it is obtained using the GetMessageExtraInfo function.
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardInput
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
MOUSEINPUT Struct
dx
is the absolute position of the mouse, or the amount of motion since the last mouse event was generated, depending on the value of the dwFlags
member. Absolute data is specified as the x coordinate of the mouse; relative data is specified as the number of pixels moved. dy
is the same as dx
but for the y-axis. If dwFlags
contains MOUSEEVENTF_WHEEL
or MOUSEEVENTF_HWHEEL
, then mouseData
specifies the amount of wheel movement. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user. One wheel click is defined as WHEEL_DELTA
which is 120. dwFlags
are a set of bit flags that specify various aspects of mouse movements and button clicks. We will look at the flags later. time
is a timestamp of the input, if it is set to 0
, then the system provides its own timestamp
. dwExtraInfo
is an additional value associated with the mouse event. It is obtained using the GetMessageExtraInfo
function.
[StructLayout(LayoutKind.Sequential)]
public struct MouseInput
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
HARDWAREINPUT Struct
uMsg
is the message generated by the input hardware. wParamL
is the low-order word of the lParam
parameter for uMsg
. wParamH
is the high-order word of the lParam
parameter for uMsg
.
[StructLayout(LayoutKind.Sequential)]
public struct HardwareInput
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
InputUnion
InputUnion
is the union parameter in the INPUT
struct
. It contains the input data for the mouse, keyboard, or hardware.
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion
{
[FieldOffset(0)] public MouseInput mi;
[FieldOffset(0)] public KeyboardInput ki;
[FieldOffset(0)] public HardwareInput hi;
}
The INPUT Struct
Used by SendInput
to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. type
is the type of the input event. It specifies which input struct
will be used from the union. The values are:
1
forMOUSEINPUT
2
forKEYBDINPUT
3
forHARDWAREINPUT
For this, we can use the InputType enum
, which we will look at later.
public struct Input
{
public int type;
public InputUnion u;
}
Flags
An enumeration type (or enum type) is a value type defined by a set of named constants of the underlying integral numeric type (int
, uint
, byte
, etc.). By default, the associated constant values of enum
members are of type int
; they start with zero and increase by one following the definition text order.
If you want an enumeration type to represent a combination of choices, define enum
members for those choices such that an individual choice is a bit field. That is, the associated values of those enum
members should be the powers of two. Then, you can use the bitwise logical operators |
or &
to combine choices or intersect combinations of choices, respectively.
The [Flags]
attribute indicates that an enumeration can be treated as a bit field; that is, a set of flags.
InputType
InputType
is a simple enum
for the different input types used in the INPUT
struct
.
[Flags]
public enum InputType
{
Mouse = 0,
Keyboard = 1,
Hardware = 2
}
Key Event Flags
KeyEventF
has the flags used by the KEYBDINPUT
struct
.
[Flags]
public enum KeyEventF
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008
}
Mouse Event Flags
MouseEventF
has the flags used by the MOUSEINPUT
struct
.
[Flags]
public enum MouseEventF
{
Absolute = 0x8000,
HWheel = 0x01000,
Move = 0x0001,
MoveNoCoalesce = 0x2000,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
VirtualDesk = 0x4000,
Wheel = 0x0800,
XDown = 0x0080,
XUp = 0x0100
}
DLL Imports
The DllImport
attribute indicates that the exposed method is from an unmanaged dynamic linked library (DLL).
Interoperability
Interoperability enables you to preserve and take advantage of existing investments in unmanaged code. Code that runs under the control of the common language runtime (CLR) is called managed code, and code that runs outside the CLR is called unmanaged code. COM, COM+, C++ components, ActiveX components, and Microsoft Windows API are examples of unmanaged code.
The .NET Framework enables interoperability with unmanaged code through platform invoke services, the System.Runtime.InteropServices
namespace, C++ interoperability, and COM interoperability (COM interop).
PInvoke
Platform invoke is a service that enables managed code to call unmanaged functions that are implemented in dynamic link libraries (DLLs), such as those in the Microsoft Windows API.
Importing the SendInput Function
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);
Importing the GetMessageExtraInfo Function
[DllImport("user32.dll")]
private static extern IntPtr GetMessageExtraInfo();
Putting It All Together
Sending Keyboard Input
First, we create an array of Input
where the ... inputs will be stored. For each input, we set the type to InputType.Keyboard
and in a KeyboardInput
object, we specify the details. In the first input, we set the scancode to 0x11
(W), set the KeyDown
and Scancode
flags. This means that we will use the scancode of the key we want to use (in that case W) and press it down. The second input is the same, but instead of pressing the button down, it is released. After the inputs are set, we send them using the SendInput
function.
Input[] inputs = new Input[]
{
new Input
{
type = (int)InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0,
wScan = 0x11, // W
dwFlags = (uint)(KeyEventF.KeyDown | KeyEventF.Scancode),
dwExtraInfo = GetMessageExtraInfo()
}
}
},
new Input
{
type = (int)InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0,
wScan = 0x11, // W
dwFlags = (uint)(KeyEventF.KeyUp | KeyEventF.Scancode),
dwExtraInfo = GetMessageExtraInfo()
}
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
By sending two sets of inputs with 2 seconds interval, we can do this:
Sending Mouse Input
For each input, we set the type to InputType.Mouse
and in a MouseInput
object, we specify the details. dx
is how much the mouse will move relatively on the x-axis. dy
is how much it will move on the y-axis. Here, we will move the mouse 100 units down and 100 units to the right, then left click. The second input will release the LMB. After the inputs are set, we send them using the SendInput
function.
Input[] inputs = new Input[]
{
new Input
{
type = (int) InputType.Mouse,
u = new InputUnion
{
mi = new MouseInput
{
dx = 100,
dy = 100,
dwFlags = (uint)(MouseEventF.Move | MouseEventF.LeftDown),
dwExtraInfo = GetMessageExtraInfo()
}
}
},
new Input
{
type = (int) InputType.Mouse,
u = new InputUnion
{
mi = new MouseInput
{
dwFlags = (uint)MouseEventF.LeftUp,
dwExtraInfo = GetMessageExtraInfo()
}
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
BONUS: Getting/Setting Absolute Mouse Coordinates
Getting Absolute Mouse Coordinates
Using the GetCursorPos
function, we can easily get the current mouse coordinates. It returns a bool
indicating if it is successful and takes a POINT struct
reference where the coordinates will be contained.
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
It is used like this:
GetCursorPos(out POINT point);
Console.WriteLine(point.X);
Console.WriteLine(point.Y);
Setting Absolute Mouse Coordinates
Using the SetCursorPos
function, we can easily set the current mouse coordinates. It returns a bool
indicating if it is successful and takes two integers for the x coordinate and y coordinate.
[DllImport("User32.dll")]
public static extern bool SetCursorPos(int x, int y);
It is used like this:
SetCursorPos(100, 100);
History
- 9th July, 2020: Initial version
- 15th July, 2020: Added demo project