// Uses User32 SendInput to send keystrokes private static void SendInput(byte[] oldKeyboardState, Queue previousEvents) { // Should be a No-Opt most of the time AddCancelModifiersForPreviousEvents(previousEvents); // SKEvents are sent as sent as 1 or 2 inputs // currentInput[0] represents the SKEvent // currentInput[1] is a KeyUp to prevent all identical WM_CHARs to be sent as one message NativeMethods.INPUT[] currentInput = new NativeMethods.INPUT[2]; // all events are Keyboard events currentInput[0].type = NativeMethods.INPUT_KEYBOARD; currentInput[1].type = NativeMethods.INPUT_KEYBOARD; // set KeyUp values for currentInput[1] currentInput[1].inputUnion.ki.wVk = (short)0; currentInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE | NativeMethods.KEYEVENTF_KEYUP; // initialize unused members currentInput[0].inputUnion.ki.dwExtraInfo = IntPtr.Zero; currentInput[0].inputUnion.ki.time = 0; currentInput[1].inputUnion.ki.dwExtraInfo = IntPtr.Zero; currentInput[1].inputUnion.ki.time = 0; // send each of our SKEvents using SendInput int INPUTSize = Marshal.SizeOf <NativeMethods.INPUT>(); // need these outside the lock below uint eventsSent = 0; int eventsTotal; // A lock here will allow multiple threads to SendInput at the same time. // This mimics the JournalHook method of using the message loop to mitigate // threading issues. There is still a theoretical thread issue with adding // to the events Queue (both JournalHook and SendInput), but we do not want // to alter the timings of the existing shipped behavior. I did not run into // problems with 2 threads on a multiproc machine lock (events.SyncRoot) { // block keyboard and mouse input events from reaching applications. bool blockInputSuccess = UnsafeNativeMethods.BlockInput(true); try { eventsTotal = events.Count; ClearGlobalKeys(); for (int i = 0; i < eventsTotal; i++) { SKEvent skEvent = (SKEvent)events.Dequeue(); currentInput[0].inputUnion.ki.dwFlags = 0; if (skEvent.wm == Interop.WindowMessages.WM_CHAR) { // for WM_CHAR, send a KEYEVENTF_UNICODE instead of a Keyboard event // to support extended ascii characters with no keyboard equivalent. // send currentInput[1] in this case currentInput[0].inputUnion.ki.wVk = (short)0; currentInput[0].inputUnion.ki.wScan = (short)skEvent.paramL; currentInput[0].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_UNICODE; currentInput[1].inputUnion.ki.wScan = (short)skEvent.paramL; // call SendInput, increment the eventsSent but subtract 1 for the extra one sent eventsSent += UnsafeNativeMethods.SendInput(2, currentInput, INPUTSize) - 1; } else { // just need to send currentInput[0] for skEvent currentInput[0].inputUnion.ki.wScan = 0; // add KeyUp flag if we have a KeyUp if (skEvent.wm == Interop.WindowMessages.WM_KEYUP || skEvent.wm == Interop.WindowMessages.WM_SYSKEYUP) { currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_KEYUP; } // Sets KEYEVENTF_EXTENDEDKEY flag if necessary if (IsExtendedKey(skEvent)) { currentInput[0].inputUnion.ki.dwFlags |= NativeMethods.KEYEVENTF_EXTENDEDKEY; } currentInput[0].inputUnion.ki.wVk = (short)skEvent.paramL; // send only currentInput[0] eventsSent += UnsafeNativeMethods.SendInput(1, currentInput, INPUTSize); CheckGlobalKeys(skEvent); } // We need this slight delay here for Alt-Tab to work on Vista when the Aero theme // is running. Although this does not look good, a delay // here actually more closely resembles the old JournalHook that processes each // event individually in the hook callback. System.Threading.Thread.Sleep(1); } // reset the keyboard back to what it was before inputs were sent, SendInupt modifies // the global lights on the keyboard (caps, scroll..) so we need to call it again to // undo those changes ResetKeyboardUsingSendInput(INPUTSize); } finally { SetKeyboardState(oldKeyboardState); // unblock input if it was previously blocked if (blockInputSuccess) { UnsafeNativeMethods.BlockInput(false); } } } // check to see if we sent the number of events we're supposed to if (eventsSent != eventsTotal) { // calls Marshal.GetLastWin32Error and sets it in the exception throw new Win32Exception(); } }
private static void SendInput(byte[] oldKeyboardState, Queue previousEvents) { int count; AddCancelModifiersForPreviousEvents(previousEvents); System.Windows.Forms.NativeMethods.INPUT[] pInputs = new System.Windows.Forms.NativeMethods.INPUT[2]; pInputs[0].type = 1; pInputs[1].type = 1; pInputs[1].inputUnion.ki.wVk = 0; pInputs[1].inputUnion.ki.dwFlags = 6; pInputs[0].inputUnion.ki.dwExtraInfo = IntPtr.Zero; pInputs[0].inputUnion.ki.time = 0; pInputs[1].inputUnion.ki.dwExtraInfo = IntPtr.Zero; pInputs[1].inputUnion.ki.time = 0; int cbSize = Marshal.SizeOf(typeof(System.Windows.Forms.NativeMethods.INPUT)); uint num2 = 0; lock (events.SyncRoot) { bool flag = UnsafeNativeMethods.BlockInput(true); try { count = events.Count; ClearGlobalKeys(); for (int i = 0; i < count; i++) { SKEvent skEvent = (SKEvent)events.Dequeue(); pInputs[0].inputUnion.ki.dwFlags = 0; if (skEvent.wm == 0x102) { pInputs[0].inputUnion.ki.wVk = 0; pInputs[0].inputUnion.ki.wScan = (short)skEvent.paramL; pInputs[0].inputUnion.ki.dwFlags = 4; pInputs[1].inputUnion.ki.wScan = (short)skEvent.paramL; num2 += UnsafeNativeMethods.SendInput(2, pInputs, cbSize) - 1; } else { pInputs[0].inputUnion.ki.wScan = 0; if ((skEvent.wm == 0x101) || (skEvent.wm == 0x105)) { pInputs[0].inputUnion.ki.dwFlags |= 2; } if (IsExtendedKey(skEvent)) { pInputs[0].inputUnion.ki.dwFlags |= 1; } pInputs[0].inputUnion.ki.wVk = (short)skEvent.paramL; num2 += UnsafeNativeMethods.SendInput(1, pInputs, cbSize); CheckGlobalKeys(skEvent); } Thread.Sleep(1); } ResetKeyboardUsingSendInput(cbSize); } finally { SetKeyboardState(oldKeyboardState); if (flag) { UnsafeNativeMethods.BlockInput(false); } } } if (num2 != count) { throw new Win32Exception(); } }