How to Use Sendinput Function C++

how to use sendinput function C++

UINT is an unsigned integer type. _In_ means the parameter is an input parameter that you send into the function. This is opposed to an output parameter, which would be something you send in, and the function would fill in.

The LPINPUT structure is defined as follows:

typedef struct tagINPUT {
DWORD type;

union
{
MOUSEINPUT mi;
KEYBDINPUT ki;
HARDWAREINPUT hi;
};
} INPUT, *PINPUT, FAR* LPINPUT;

So it looks like a DWORD coupled with a union of some other structures. Refer to WinUser.h for more.

DWORD is a 32-bit unsigned integer (source):

A DWORD is a 32-bit unsigned integer (range: 0 through 4294967295
decimal). Because a DWORD is unsigned, its first bit (Most Significant
Bit (MSB)) is not reserved for signing. This type is declared as
follows: typedef unsigned long DWORD, *PDWORD, *LPDWORD;

MOUSEINPUT

typedef struct tagMOUSEINPUT {
LONG dx;
LONG dy;
DWORD mouseData;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MOUSEINPUT, *PMOUSEINPUT, FAR* LPMOUSEINPUT;

KEYBDINPUT

typedef struct tagKEYBDINPUT {
WORD wVk;
WORD wScan;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT, FAR* LPKEYBDINPUT;

HARDWAREINPUT

typedef struct tagHARDWAREINPUT {
DWORD uMsg;
WORD wParamL;
WORD wParamH;
} HARDWAREINPUT, *PHARDWAREINPUT, FAR* LPHARDWAREINPUT;

WORD, LONG, ULONG, and ULONG_PTR are all well-defined on the MSDN page (See the column on the right)

Here's an example of using SendInput that can be easily found via Googling (Source):

//
// keystroke.c - Pauses, then simulates a key press
// and release of the "A" key.
//
// Written by Ted Burke - last updated 17-4-2012
//
// To compile with MinGW:
//
// gcc -o keystroke.exe keystroke.c
//
// To run the program:
//
// keystroke.exe
//
// ...then switch to e.g. a Notepad window and wait
// 5 seconds for the A key to be magically pressed.
//

// Because the SendInput function is only supported in
// Windows 2000 and later, WINVER needs to be set as
// follows so that SendInput gets defined when windows.h
// is included below.
#define WINVER 0x0500
#include <windows.h>

int main()
{
// This structure will be used to create the keyboard
// input event.
INPUT ip;

// Pause for 5 seconds.
Sleep(5000);

// Set up a generic keyboard event.
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0; // hardware scan code for key
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;

// Press the "A" key
ip.ki.wVk = 0x41; // virtual-key code for the "a" key
ip.ki.dwFlags = 0; // 0 for key press
SendInput(1, &ip, sizeof(INPUT));

// Release the "A" key
ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT));

// Exit normally
return 0;
}

The other part of your question:

Also, type, ki.wScan, ki.time, ki.dwExtraInfo, ki.wVk, ki.dwFlags
What do objects above mean

I believe you're referring to this code from the MSDN page:

// IMPORTANT: Current keyboard layout 0xf0010413 (Netherland with USA kbd)!!!!!!!
WORD vkCode = 0x36; // '6'
INPUT keyEvent = {0};
keyEvent.type = INPUT_KEYBOARD;
keyEvent.ki.wVk = vkCode;
keyEvent.ki.wScan = MapVirtualKeyEx(vkCode, 0, (HKL)0xf0010413);
SendInput(1, &keyEvent, sizeof(keyEvent));

That code was posted by another use on the page; it's not part of the documentation. The user simply created an LPINPUT structure named keyEvent, and then accessed the KEYBDEVENT part of the structure:

KEYBDINPUT      ki;

How does sendInput() function work in C#?

All relevant information can be found in the documentation about the function.

The function returns the number of events that it successfully
inserted into the keyboard or mouse input stream. If the function
returns zero, the input was already blocked by another thread.

So how it works is that it inserts the events in the INPUT structures (which are fed to the function through the parameters) serially into the keyboard or mouse input stream and then returns the number of those events that are successfully inserted..

Simulating input using the SendInput function

Try

SendInput(INPUTS, inputArray, sizeof(INPUT));

and check that the return value is non-zero. In fact, it should equal to the number of inputs sent, in this case 4.

The function may fail if it is blocked by another thread or UIPI (User Interface Privilege Isolation)

To avoid other problems you may want to check the current keyboard state as well as SendInput adds to the input stream and other keys being pressed while this is running could interfere with it.

Out of curiosity what action are you trying to simulate? It's generally not a very graceful practice to "fake" user input to any application.

How to use SendInput to simulate the UP arrow key press (or other extended keys)

A few notes about SendInput(), in relation to what you tried to do.

  • This function accepts and array of INPUT structures. You need to pass all Key presses in a single call to this function. This include a Key and Key modifiers (Control, Shift, ALT, Menu et.)
  • The input is sent to the current Input recipient, so you need to make sure the Window that is meant to receive the key presses is Focused (or its Parent Window or the Main Window, i.e., the window that actually processes the key presses)
  • If the target Window belongs to a different Thread, the call to SetFocus() may fail, so we attach to that Thread beforehand.
  • If the parent Main Window is minimized, you need to bring it up beforehand, to be sure it will receive focus and then our input.

Here, I'm using GetCurrentThreadId() and compare with GetWindowThreadProcessId(), to verify whether the caller Thread and the target Thread are different.

If they're not the same, then AttachThreadInput() is called.

If the target Main Window is minimized (see the call to IsIconic()), it's restored, calling SetWindowPlacement() and brought to the foreground using BringWindowToTop().

Then a call to SetFocus() moves the focus to the target handle (assuming it can receive focus, that is - the function should return success anyway).

In the end, you collect all the Keys you need to send in different INPUT structs (each Key Modifiers in its own struct) and make a single call to SendInput().


For example:

Send the Up key to the target handle:

IntPtr handle = [The selected handle];
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Up, null);

Send Control + Shift + Home:

var modifiers = new[] { Keys.ShiftKey, Keys.ControlKey};
bool result = NativeMethods.SendKeyboardInput(handle, Keys.Home, modifiers);

This is how it works:

SendInput test

A standard PictureBox cannot be focused, so you'll see a Button flicker when SetFocus() is called

Sample Project for testing (Google Drive)

NativeMethods:

internal class NativeMethods {

public static bool SendKeyboardInput(IntPtr hWnd, Keys key, Keys[] modifiers = null, int delay = 0)
{
if (hWnd != IntPtr.Zero) {
uint targetThreadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
uint currentThreadID = GetCurrentThreadId();

if (targetThreadID != currentThreadID) {
try {
if (!AttachThreadInput(currentThreadID, targetThreadID, true)) return false;
var parentWindow = GetAncestor(hWnd, GetAncestorFlags.GA_ROOT);
if (IsIconic(parentWindow)) {
if (!RestoreWindow(parentWindow)) return false;
}

if (!BringWindowToTop(parentWindow)) return false;
if (SetFocus(hWnd) == IntPtr.Zero) return false;
}
finally {
AttachThreadInput(currentThreadID, targetThreadID, false);
}
}
else {
SetFocus(hWnd);
}
}

var flagsKeyDw = IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : KeyboardInputFlags.KeyDown;
var flagsKeyUp = KeyboardInputFlags.KeyUp | (IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : 0);

var inputs = new List<INPUT>();
var input = new INPUT(SendInputType.InputKeyboard);

// Key Modifiers Down
if (!(modifiers is null)) {
foreach (var modifier in modifiers) {
input.Union.Keyboard.Flags = KeyboardInputFlags.KeyDown;
input.Union.Keyboard.VirtKeys = (ushort)modifier;
inputs.Add(input);
}
}

// Key Down
input.Union.Keyboard.Flags = flagsKeyDw | KeyboardInputFlags.Unicode;
input.Union.Keyboard.VirtKeys = (ushort)key;
inputs.Add(input);

// Key Up
input.Union.Keyboard.Flags = flagsKeyUp | KeyboardInputFlags.Unicode;
input.Union.Keyboard.VirtKeys = (ushort)key;
inputs.Add(input);

// Key Modifiers Up
if (!(modifiers is null)) {
foreach (var modifier in modifiers) {
input.Union.Keyboard.Flags = KeyboardInputFlags.KeyUp;
input.Union.Keyboard.VirtKeys = (ushort)modifier;
inputs.Add(input);
}
}

uint sent = SendInput((uint)inputs.Count(), inputs.ToArray(), Marshal.SizeOf<INPUT>());
return sent > 0;
}

private static Keys[] extendedKeys = { Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Home, Keys.End, Keys.Prior, Keys.Next, Keys.Insert, Keys.Delete };
private static bool IsExtendedKey(Keys key) => extendedKeys.Contains(key);

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
[StructLayout(LayoutKind.Sequential)]
public struct INPUT {
public SendInputType InputType;
public InputUnion Union;

public INPUT(SendInputType type) {
InputType = type;
Union = new InputUnion();
}
}

public enum SendInputType : uint {
InputMouse = 0,
InputKeyboard = 1,
InputHardware = 2
}

[StructLayout(LayoutKind.Explicit)]
public struct InputUnion {
[FieldOffset(0)]
public MOUSEINPUT Mouse;

[FieldOffset(0)]
public KEYBDINPUT Keyboard;

[FieldOffset(0)]
public HARDWAREINPUT Hardware;
}

[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT {
public int dx;
public int dy;
public uint mouseData;
public MouseEventdwFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT {
public ushort VirtKeys;
public ushort wScan;
public KeyboardInputFlags Flags;
public uint time;
public IntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT {
public int uMsg;
public short wParamL;
public short wParamH;
}

[Flags]
public enum MouseEventdwFlags : 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
}

[Flags]
public enum KeyboardInputFlags : uint {
KeyDown = 0x0,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Scancode = 0x0008,
Unicode = 0x0004
}

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowplacement
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT {
public int length;
public WplFlags flags;
public SW_Flags showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}

public enum WplFlags : uint {
WPF_ASYNCWINDOWPLACEMENT = 0x0004, // If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
WPF_RESTORETOMAXIMIZED = 0x0002, // The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior.
// This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member.
WPF_SETMINPOSITION = 0x0001 // The coordinates of the minimized window may be specified. This flag must be specified if the coordinates are set in the ptMinPosition member.
}

[Flags]
public enum SW_Flags : uint {
SW_HIDE = 0X00,
SW_SHOWNORMAL = 0x01,
SW_MAXIMIZE = 0x03,
SW_SHOWNOACTIVATE = 0x04,
SW_SHOW = 0x05,
SW_MINIMIZE = 0x06,
SW_RESTORE = 0x09,
SW_SHOWDEFAULT = 0x0A,
SW_FORCEMINIMIZE = 0x0B
}

public enum GetAncestorFlags : uint {
GA_PARENT = 1, // Retrieves the parent window.This does not include the owner, as it does with the GetParent function.
GA_ROOT = 2, // Retrieves the root window by walking the chain of parent windows.
GA_ROOTOWNER = 3 // Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
}

[StructLayout(LayoutKind.Sequential)]
public class POINT {
public int x;
public int y;

public POINT(int x, int y) {
this.x = x;
this.y = y;
}
public Point ToPoint() => new Point(this.x, this.y);
public PointF ToPointF() => new PointF((float)this.x, (float)this.y);
public POINT FromPoint(Point p) => new POINT(p.X, p.Y);
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;

public RECT(int left, int top, int right, int bottom) {
Left = left; Top = top; Right = right; Bottom = bottom;
}

public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);

public RECT FromRectangle(RectangleF rectangle) => FromRectangle(Rectangle.Round(rectangle));
public RECT FromRectangle(Rectangle rectangle) => new RECT() {
Left = rectangle.Left,
Top = rectangle.Top,
Bottom = rectangle.Bottom,
Right = rectangle.Right
};
public RECT FromXYWH(int x, int y, int width, int height) => new RECT(x, y, x + width, y + height);
}

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowplacement
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowPlacement(IntPtr hWnd, [In, Out] ref WINDOWPLACEMENT lpwndpl);

[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId);

// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint GetCurrentThreadId();

// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AttachThreadInput([In] uint idAttach, [In] uint idAttachTo, [In, MarshalAs(UnmanagedType.Bool)] bool fAttach);

[ResourceExposure(ResourceScope.None)]
[DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
internal static extern IntPtr GetAncestor(IntPtr hWnd, GetAncestorFlags flags);

[DllImport("user32.dll")]
internal static extern bool IsIconic(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
internal static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SetFocus(IntPtr hWnd);

//https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint SendInput(uint nInputs, [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize);

public static bool RestoreWindow(IntPtr hWnd)
{
var wpl = new WINDOWPLACEMENT() {
length = Marshal.SizeOf<WINDOWPLACEMENT>()
};
if (!GetWindowPlacement(hWnd, ref wpl)) return false;

wpl.flags = WplFlags.WPF_ASYNCWINDOWPLACEMENT;
wpl.showCmd = SW_Flags.SW_RESTORE;
return SetWindowPlacement(hWnd, ref wpl);
}
}

Sending Two or more chars using SendInput

The first parameter of SendInput() specifies how many INPUT structures you are passing in. You are only passing in 1, but you are telling SendInput() that you are passing in 2.

You cannot specify two separate virtual keys in a single INPUT. You need to declare an array of multiple INPUTs, 2 INPUTs for each virtual key - one for the keydown event, and one for the keyup event. So, in your example, you actually need 4 INPUTs to send 2 virtual keys, as shown in @user4581301's answer.

Now, regarding KEYEVENTF_UNICODE, you don't use virtual keys with it, you use actual Unicode codepoints instead, where they are specified using UTF-16 codeunits, one per INPUT. So that means if you want to send a Unicode codepoint that requires a UTF-16 surrogate pair, you need 2 sets of down/up INPUTs, one set for the high surrogate, and one set for the low surrogate. That caveat is NOT mentioned in the SendInput() documentation, but it is implied by the fact that the vScan field is a 16bit WORD, and that KEYEVENTF_UNICODE events generate WM_CHAR messages, which passes UTF-16 surrogate codeunits as separate messages.

So, to send a string of Unicode characters using KEYEVENTF_UNICODE, you can do something like this:

#include <vector>
#include <string>

void SendInputString(const std::wstring &str)
{
int len = str.length();
if (len == 0) return;

std::vector<INPUT> in(len*2);
ZeroMemory(&in[0], in.size()*sizeof(INPUT));

int i = 0, idx = 0;
while (i < len)
{
WORD ch = (WORD) str[i++];

if ((ch < 0xD800) || (ch > 0xDFFF))
{
in[idx].type = INPUT_KEYBOARD;
in[idx].ki.wScan = ch;
in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
++idx;

in[idx] = in[idx-1];
in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
++idx;
}
else
{
in[idx].type = INPUT_KEYBOARD;
in[idx].ki.wScan = ch;
in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
++idx;

in[idx].type = INPUT_KEYBOARD;
in[idx].ki.wScan = (WORD) str[i++];
in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
++idx;

in[idx] = in[idx-2];
in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
++idx;

in[idx] = in[idx-2];
in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
++idx;
}
}

SendInput(in.size(), &in[0], sizeof(INPUT));
}


Related Topics



Leave a reply



Submit