Send Keys Through Sendinput in User32.Dll

Send keys through SendInput in user32.dll

You aren't setting the flags and scan fields, depending on the keystrokes desired, you will need to set these correctly to get the OS to recognize the keys correctly.

You might consider using the Input Simulator library, since it already does what you want and you don't have to recreate the wheel. Just make sure to look through the forums as there are some good patches in there that need to be set, since the author abandoned the project in 2009. It's a good library nonetheless.

SendInput function from user32.dll doesn't work if Task Manager is opened

I ran into an issue quite recently using this method. It is down to the permissions your application has when it is running and will only happen when Task Manager is the focussed window. If you run your application as Administrator you should have the permissions required to interact with Task Manager.

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);
}
}

Using User32.dll SendMessage To Send Keys With ALT Modifier

Another solution. It doesn't appear you are passing anything for the lparam of the WM_SYSKEYDOWN. Yet the docs, clearly suggest that bit 29 of the lparam needs to be set to indicate the ALT key was pressed.

ushort action = (ushort)WM_SYSKEYDOWN;
ushort key = (ushort)System.Windows.Forms.Keys.F1;
uint lparam = (0x01 << 28);
SendMessage(hWnd, action, key, lparam);

Send keystroke to application in c# (sendkeys, postmessage, sendmessage all not working)

There is lot of trial and error with that, to get it working

Here is a bit of code I posted before, you might wanna give a try (and there is some more info attached)...

Pinvoke SetFocus to a particular control

Try setting focus first (using the mechanism mentioned) - and then using SendKeys or SendInput.

Here is some detailed code for SendInput...

How to send a string to other application including Microsoft Word



Related Topics



Leave a reply



Submit