Click here to Skip to main content
15,923,164 members
Articles / Programming Languages / C#

How to Send Inputs using C#

Rate me:
Please Sign up or sign in to vote.
4.97/5 (29 votes)
15 Jul 2020CPOL6 min read 62.8K   2.6K   54   19
A guide on how to use the Windows API and C# to send Inputs
In this article, we will explore ways to send inputs using C#. This can be used for automating tasks or just messing around.

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.

C#
[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.

C#
[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.

C#
[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.

C#
[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 for MOUSEINPUT
  • 2 for KEYBDINPUT
  • 3 for HARDWAREINPUT

For this, we can use the InputType enum, which we will look at later.

C#
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.

C#
[Flags]
public enum InputType
{
    Mouse = 0,
    Keyboard = 1,
    Hardware = 2
}

Key Event Flags

KeyEventF has the flags used by the KEYBDINPUT struct.

C#
[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.

C#
[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

C#
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);

Importing the GetMessageExtraInfo Function

C#
[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.

C#
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:

Image 1

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.

C#
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)));

Image 2

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.

C#
[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:

C#
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.

C#
[DllImport("User32.dll")]
public static extern bool SetCursorPos(int x, int y);

It is used like this:

C#
SetCursorPos(100, 100);

History

  • 9th July, 2020: Initial version
  • 15th July, 2020: Added demo project

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Bulgaria Bulgaria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIs there a way to use modifiers? Pin
Omer Hijazi7-Apr-21 19:52
Omer Hijazi7-Apr-21 19:52 
QuestionGetMessageExtraInfo? Pin
ShawnVN1-Mar-21 21:32
ShawnVN1-Mar-21 21:32 
QuestionMessage Removed Pin
22-Aug-20 8:35
huhuhhuhuh22-Aug-20 8:35 
QuestionHow does that compare to the recomended SendKeys method? Pin
Andreas Saurwein14-Jul-20 1:34
Andreas Saurwein14-Jul-20 1:34 
AnswerRe: How does that compare to the recomended SendKeys method? Pin
Bojidar Qnkov14-Jul-20 7:04
Bojidar Qnkov14-Jul-20 7:04 
GeneralRe: How does that compare to the recomended SendKeys method? Pin
Bojidar Qnkov14-Jul-20 7:06
Bojidar Qnkov14-Jul-20 7:06 
GeneralRe: How does that compare to the recomended SendKeys method? Pin
Andreas Saurwein14-Jul-20 7:13
Andreas Saurwein14-Jul-20 7:13 
GeneralRe: How does that compare to the recomended SendKeys method? Pin
Bojidar Qnkov14-Jul-20 7:40
Bojidar Qnkov14-Jul-20 7:40 
AnswerRe: How does that compare to the recomended SendKeys method? Pin
Martin Garmendia16-Jul-20 0:24
Martin Garmendia16-Jul-20 0:24 
QuestionDemo project? Pin
eblaschka11-Jul-20 9:26
eblaschka11-Jul-20 9:26 
AnswerRe: Demo project? Pin
Bojidar Qnkov11-Jul-20 10:14
Bojidar Qnkov11-Jul-20 10:14 
GeneralRe: Demo project? Pin
P Conny M Westh12-Jul-20 3:30
P Conny M Westh12-Jul-20 3:30 
GeneralRe: Demo project? Pin
Bojidar Qnkov13-Jul-20 0:58
Bojidar Qnkov13-Jul-20 0:58 
GeneralRe: Demo project? Pin
P Conny M Westh15-Jul-20 12:23
P Conny M Westh15-Jul-20 12:23 
GeneralRe: Demo project? Pin
F Margueirat14-Jul-20 7:10
F Margueirat14-Jul-20 7:10 
Bojidar

I see this quite frequently. For someone that is familiar with the code (like you) it's very straightforward how to copy and paste into a CS file and get it to run. But for someone that is new, it might be very frustrating when things are not copied the exact way it is supposed (e.g. "does this part of the code goes in the class definition or inside a method?" or "what package I am missing?") and get a lot of compilation errors.

A small Visual Studio solution with some demo code, makes it much easier for beginners to run the code and start experimenting with it. Is a little extra work but the result is more happy readers => more 5* rates. Isn't that what more contributors want for their articles Wink | ;) ?
GeneralRe: Demo project? Pin
Bojidar Qnkov14-Jul-20 7:43
Bojidar Qnkov14-Jul-20 7:43 
GeneralRe: Demo project? Pin
Bojidar Qnkov15-Jul-20 5:31
Bojidar Qnkov15-Jul-20 5:31 
AnswerRe: Demo project? Pin
P Conny M Westh11-Jul-20 10:24
P Conny M Westh11-Jul-20 10:24 
GeneralMy vote of 5 Pin
Super Lloyd9-Jul-20 13:07
Super Lloyd9-Jul-20 13:07 
GeneralRe: My vote of 5 Pin
Bojidar Qnkov11-Jul-20 5:14
Bojidar Qnkov11-Jul-20 5:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.