I'm trying to acquire keyboard input using GetRawInputData and C#. I don't seem to be able to actually read the RAWINPUTDATA fields, no matter what I use for the definition of the call.
I have tried this:
In response to the WM_INPUT message, I try this...
void ProcessRawInput(Message message)
{
uint dwSize = 0;
uint retval;
User32.RAWINPUT rawInput;
retval = User32.GetRawInputData(message.LParam, User32.RAWINPUT.RID_HEADER, out rawInput, ref dwSize, Marshal.SizeOf<User32.RAWINPUTHEADER>());
if (rawInput.header.dwType == (int)User32.RIM_TYPE.RIM_TYPEKEYBOARD)
{
}
}
It seems to read some data into the header structure, but nothing in the RAWINPUTDATA structure.
I tried something like:
if (rawInput.header.dwType == (int)User32.RIM_TYPE.RIM_TYPEKEYBOARD)
{
// set dwSize = Marshal.SizeOf<User32.RAWINPUT>()
retval = User32.GetRawInputData(message.LParam, User32.RAWINPUT.RID_INPUT, out rawInput, ref dwSize, Marshal.SizeOf<User32.RAWINPUTHEADER>());
}
But the only difference seems to be a huge value returned as retval.
I cannot seem to find a useable example (in C#).
My definitions and structures look like this:
[DllImport("User32.dll", SetLastError = true)]
internal static extern uint GetRawInputData(IntPtr hRawInput, uint uiCommand, out RAWINPUT pData, ref uint pcbSize, int cbSizeHeader);
[StructLayout(LayoutKind.Sequential)]
public struct RAWINPUT
{
public const int RID_INPUT = 0x10000003,
RID_HEADER = 0x10000005;
public RAWINPUTHEADER header;
public RAWINPUTDATA data;
}
[StructLayout(LayoutKind.Sequential)]
public struct RAWINPUTHEADER
{
public int dwType;
public int dwSize;
public IntPtr hDevice;
public IntPtr wParam;
}
[StructLayout(LayoutKind.Explicit)]
public struct RAWINPUTDATA
{
[FieldOffset(0)]
public RAWMOUSE Mouse;
[FieldOffset(0)]
public RAWKEYBOARD Keyboard;
[FieldOffset(0)]
public RAWHID HID;
}
[StructLayout(LayoutKind.Sequential)]
public struct RAWKEYBOARD
{
public ushort MakeCode;
public ushort Flags;
public ushort Reserved;
public ushort VKey;
public uint Message;
public ulong ExtraInformation;
}
Related
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 am running a c# application that makes use of the sendinputs method in windows to mimic keystrokes. It works fine when I am remoted into the instance, but as soon as I log out it throws an exception. Can anyone offer any thoughts on why this is happening, and how to avoid it?
UPDATE::
here is some sample code that can reproduce the issue.
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ConsoleApplication6
{
class Program
{
class SendKey
{
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public short wVk;
public short wScan;
public int dwFlags;
public int time;
public int dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
private struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
};
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT no;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
};
[DllImport("user32.dll")]
private extern static void SendInput(int nInputs, ref INPUT pInputs, int cbsize);
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
private extern static int MapVirtualKey(int wCode, int wMapType);
private const int INPUT_KEYBOARD = 1;
private const int KEYEVENTF_KEYDOWN = 0x0;
private const int KEYEVENTF_KEYUP = 0x2;
private const int KEYEVENTF_EXTENDEDKEY = 0x1;
private void Send(Keys key, bool isEXTEND)
{
INPUT inp = new INPUT();
inp.type = INPUT_KEYBOARD;
inp.ki.wVk = (short)key;
inp.ki.wScan = (short)MapVirtualKey(inp.ki.wVk, 0);
inp.ki.dwFlags = ((isEXTEND) ? (KEYEVENTF_EXTENDEDKEY) : 0x0) | KEYEVENTF_KEYDOWN;
inp.ki.time = 0;
inp.ki.dwExtraInfo = 0;
SendInput(1, ref inp, Marshal.SizeOf(inp));
System.Threading.Thread.Sleep(100);
inp.ki.dwFlags = ((isEXTEND) ? (KEYEVENTF_EXTENDEDKEY) : 0x0) | KEYEVENTF_KEYUP;
SendInput(1, ref inp, Marshal.SizeOf(inp));
}
static void Main()
{
while(true)
{
System.Threading.Thread.Sleep(2000);
SendKey s = new SendKey();
s.Send(Keys.Z, false);
s.Send(Keys.Left, true);
}
}
}
}
}
if you build this and just run it on a free windows server ec2 instance with notepad in focus it will print zzzzzz continuously while you are remoted into the instance, however, as soon as you remote out of the instance, it will stop printing until you log back in.
i'm new and i need your help resolving this issue.
I'm trying to create a simple debugger to understand how debuggers works and how is loaded exes in memory. I have already written the code which works as well, but now there is the problem: when i try to call WaitForDebugEvent (a kernel32 function) to get the debug event it works, in fact the debug_event variable is written, but this function clears all the variables in my application. So it clear also:
this (current form)
EventArgs (arguments of my form load function)
object sender (the object who called the function)
So i can't continue executing my app because all the vars were deleted. I wouldn't think this is a kernel32 or a Visual Studio bug...
This is the code:
(Structs and imports got from pInvoke.net and MSDN)
[DllImport("kernel32.dll")]
static extern bool DebugActiveProcess(uint dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "WaitForDebugEvent")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WaitForDebugEvent([In] ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);
[DllImport("kernel32.dll")]
static extern bool ContinueDebugEvent(uint dwProcessId, uint dwThreadId, uint dwContinueStatus);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DebugActiveProcessStop([In] int Pid);
public struct DEBUG_EVENT
{
public int dwDebugEventCode;
public int dwProcessId;
public int dwThreadId;
public struct u
{
public EXCEPTION_DEBUG_INFO Exception;
public CREATE_THREAD_DEBUG_INFO CreateThread;
public CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
public EXIT_THREAD_DEBUG_INFO ExitThread;
public EXIT_PROCESS_DEBUG_INFO ExitProcess;
public LOAD_DLL_DEBUG_INFO LoadDll;
public UNLOAD_DLL_DEBUG_INFO UnloadDll;
public OUTPUT_DEBUG_STRING_INFO DebugString;
public RIP_INFO RipInfo;
};
};
[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_DEBUG_INFO
{
public EXCEPTION_RECORD ExceptionRecord;
public uint dwFirstChance;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXCEPTION_RECORD
{
public uint ExceptionCode;
public uint ExceptionFlags;
public IntPtr ExceptionRecord;
public IntPtr ExceptionAddress;
public uint NumberParameters;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 15, ArraySubType = UnmanagedType.U4)]
public uint[] ExceptionInformation;
}
public delegate uint PTHREAD_START_ROUTINE(IntPtr lpThreadParameter);
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_THREAD_DEBUG_INFO
{
public IntPtr hThread;
public IntPtr lpThreadLocalBase;
public PTHREAD_START_ROUTINE lpStartAddress;
}
//public delegate uint PTHREAD_START_ROUTINE(IntPtr lpThreadParameter);
[StructLayout(LayoutKind.Sequential)]
public struct CREATE_PROCESS_DEBUG_INFO
{
public IntPtr hFile;
public IntPtr hProcess;
public IntPtr hThread;
public IntPtr lpBaseOfImage;
public uint dwDebugInfoFileOffset;
public uint nDebugInfoSize;
public IntPtr lpThreadLocalBase;
public PTHREAD_START_ROUTINE lpStartAddress;
public IntPtr lpImageName;
public ushort fUnicode;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXIT_THREAD_DEBUG_INFO
{
public uint dwExitCode;
}
[StructLayout(LayoutKind.Sequential)]
public struct EXIT_PROCESS_DEBUG_INFO
{
public uint dwExitCode;
}
[StructLayout(LayoutKind.Sequential)]
public struct LOAD_DLL_DEBUG_INFO
{
public IntPtr hFile;
public IntPtr lpBaseOfDll;
public uint dwDebugInfoFileOffset;
public uint nDebugInfoSize;
public IntPtr lpImageName;
public ushort fUnicode;
}
[StructLayout(LayoutKind.Sequential)]
public struct UNLOAD_DLL_DEBUG_INFO
{
public IntPtr lpBaseOfDll;
}
[StructLayout(LayoutKind.Sequential)]
public struct OUTPUT_DEBUG_STRING_INFO
{
[MarshalAs(UnmanagedType.LPStr)]
public string lpDebugStringData;
public ushort fUnicode;
public ushort nDebugStringLength;
}
[StructLayout(LayoutKind.Sequential)]
public struct RIP_INFO
{
public uint dwError;
public uint dwType;
}
And the main loop of debugger:
private void Form1_Load(object sender, EventArgs e)
{
DebugActiveProcess((uint)Process.GetProcessesByName("notepad")[0].Id);
DEBUG_EVENT debug_event = new DEBUG_EVENT();
CONTEXT context = new CONTEXT();
context.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL;
while (true)
{
unchecked
{
if (WaitForDebugEvent(ref debug_event, (uint)double.PositiveInfinity))
{
...
ContinueDebugEvent((uint)debug_event.dwProcessId, (uint)debug_event.dwThreadId, (uint)0x10002);
}
}
}
}
What can i do? Thanks in advance...
EDIT:
I have rewritten the code in C++ to see if the problem there was there too. But there was no issue... so i think the problem lays only in C#. This is the code in C++:
IMPORTS:
#include <windows.h>
#include <tlhelp32.h>
#include <msclr\marshal_cppstd.h>
CODE:
private: System::Void DebuggerForm_Load(System::Object^ sender, System::EventArgs^ e) {
std::wstring processName = msclr::interop::marshal_as<std::wstring, String^>("myExe.exe");
DWORD id = getProcessId(processName);
if (id == 0) std::exit(0);
DebugActiveProcess(id);
DEBUG_EVENT debug_event = { 0 };
while (true)
{
if (WaitForDebugEvent(&debug_event, INFINITE)) {
// TODO
ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);
}
}
}
DWORD getProcessId(const std::wstring& processName)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (!processName.compare(processInfo.szExeFile))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
CloseHandle(processesSnapshot);
return 0;
}
First of all, You should enable the SE_DEBUG_NAME privilege on the targeted process:
(SE_DEBUG_NAME = "SeDebugPrivilege")
Use OpenProcessToken with TOKEN_ADJUST_PRIVILEGES and TOKEN_QUERY access flags.
Use LookupPrivilegeValue to retrieve the LUID of the SE_DEBUG_NAME privilege name.
Use AdjustTokenPrivileges to enable the SE_DEBUG_NAME privilege.
CloseHandle
For the 3rd step you need a TOKEN_PRIVILEGES structure, where the PrivilegesCount field must set to 1. Also You must set the first(zero index) item of the Privilege field (Luid: see 2nd step, Attributes: SE_PRIVILEGE_ENABLED)
The DEBUG_EVENT structure:
The u field of the structure is an union, it means that it only contains one of the listed structs. Try to use LayoutKind.Explicit and decorate every field with the attribute FieldOffset to define the correct offset inside the structure. Try this:
[StructLayout(LayoutKind.Explicit)]
public struct DEBUG_EVENT
{
[FieldOffset(0)]
public int dwDebugEventCode;
[FieldOffset(4)]
public int dwProcessId;
[FieldOffset(8)]
public int dwThreadId;
[FieldOffset(12)]
[StructLayout(LayoutKind.Explicit)]
public struct u {
[FieldOffset(0)]
public EXCEPTION_DEBUG_INFO Exception;
[FieldOffset(0)]
public CREATE_THREAD_DEBUG_INFO CreateThread;
[FieldOffset(0)]
public CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
[FieldOffset(0)]
public EXIT_THREAD_DEBUG_INFO ExitThread;
[FieldOffset(0)]
public EXIT_PROCESS_DEBUG_INFO ExitProcess;
[FieldOffset(0)]
public LOAD_DLL_DEBUG_INFO LoadDll;
[FieldOffset(0)]
public UNLOAD_DLL_DEBUG_INFO UnloadDll;
[FieldOffset(0)]
public OUTPUT_DEBUG_STRING_INFO DebugString;
[FieldOffset(0)]
public RIP_INFO RipInfo;
}
};
I want to use API from Omron V4KU, the documentation described like this :
Original c# code :
const string DllLocation = #"..\..\Libs\Omron\OMCR.dll";
[DllImport(DllLocation)]
public static extern LPCOMCR OMCR_OpenDevice(string lpcszDevice, LPCOMCR_OPTION lpcOption);
public void Start()
{
var lpcOption = new LPCOMCR_OPTION();
var result = OMCR_OpenDevice(null, lpcOption); // error method's type signature is not pinvoke compatible
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR
{
public string lpcszDevice;
public IntPtr hDevice;
public uint lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct LPCOMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
if I missed or wrong in writing code?
sorry, my english is bad. thanks for help.
Start by defining the union structure correctly:
// OMCR_OPTION.COM
[StructLayout(LayoutKind.Sequential)]
public struct OmcrCom
{
public IntPtr Reserved0;
public uint BaudRate;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
public IntPtr Reserved1;
public IntPtr Reserved2;
}
// OMCR_OPTION.USB
[StructLayout(LayoutKind.Sequential)]
public struct OmcrUsb
{
public uint Reserved0;
public uint Reserved1;
public uint Reserved2;
public uint Reserved3;
}
// OMCR_OPTION (union of COM and USB)
[StructLayout(LayoutKind.Explicit)]
public struct OmcrOptions
{
[FieldOffset(0)]
public OmcrCom Com;
[FieldOffset(0)]
public OmcrUsb Usb;
}
// OMCR
[StructLayout(LayoutKind.Sequential)]
public struct OmcrDevice
{
public string Device;
public IntPtr DeviceHandle;
public IntPtr DevicePointer;
}
[DllImport(dllName: DllLocation, EntryPoint = "OMCR_OpenDevice"]
public static extern IntPtr OmcrOpenDevice(string type, ref OmcrOptions options);
And then call the method, something like:
var options = new OmcrOptions();
options.Com.BaudRate = 115200; // or whatever you need to set
var type = "COM"; // is this USB/COM? not sure
OmcrDevice device;
var devicePtr = OmcrOpenDevice(type, ref options);
if (devicePtr == IntPtr.Zero)
device = (OmcrDevice)Marshal.PtrToStructure(devicePtr, typeof(OmcrDevice));
Well, for one, the documentation is asking you to pass LPCOMCR_OPTION as a pointer - you're passing it as a value. Using ref should help. There's another problem, though, and that's the return value - again, you're trying to interpret it as a value, while the docs say it's a pointer. However, this is a lot trickier than the first error - as far as I'm aware, your only options are using a C++/CLI interop library, or expecting IntPtr as a return value. In any case, you need to handle proper deallocation of the memory you get this way.
You need to do it like this:
[StructLayout(LayoutKind.Sequential)]
public struct OMCR
{
[MarshalAs(UnmanagedType.LPStr)]
public string lpcszDevice;
public IntPtr hDevice;
public IntPtr lpcDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct OMCR_OPTION
{
public uint dwReserved0;
public uint dwReserved1;
public uint dwReserved2;
public uint dwReserved3;
}
[DllImport(DllLocation, CallingConvention = CallingConvention.???,
SetLastError = true)]
public static extern IntPtr OMCR_OpenDevice(string lpcszDevice,
ref OMCR_OPTION lpcOption);
You need to replace CallingConvention.??? with the appropriate calling convention. We cannot tell from the question what that is. You will have to find out by reading the header file.
The return value is a pointer to OMCR. You need to hold on to this pointer and pass it to OMCR_CloseDevice when you are finished with it.
In order to obtain an OMCR value you would do the following:
OMCR_OPTION Option = new OMCR_OPTION(); // not sure how to initialize this
IntPtr DevicePtr = OMCR_OpenDevice(DeviceType, ref Option);
if (DevicePtr == IntPtr.Zero)
throw new Win32Exception();
OMCR Device = (OMCR)Marshal.PtrToStructure(DevicePtr, typeof(OMCR));
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)));