/// <summary> /// Send array of System.Window.Forms.Keys to client as keypresses. Restores and activates window. /// </summary> /// <param name="client">Target client.</param> /// <param name="keys">System.Window.Forms.Keys to send.</param> /// <returns>True if all keys were successfully sent.</returns> public static bool SendKeys(int client, Keys[] keys) { if (keys.Length == 0) return false; ClientInfo ci; if (ClientInfoCollection.GetClient(client, out ci)) { NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[keys.Length * 2]; NativeMethods.KEYBDINPUT kbi = new NativeMethods.KEYBDINPUT(); if (!ci.PrepareWindowForInput()) { ci.DetachFromWindow(); return false; } for (int x = 0; x < keys.Length; x++) { kbi.dwFlags = 0; kbi.wVk = (ushort)keys[x]; inputs[x * 2].mkhi.ki = kbi; inputs[x * 2].type = NativeMethods.INPUT_KEYBOARD; kbi.dwFlags = NativeMethods.KEYEVENTF_KEYUP; inputs[x * 2 + 1].mkhi.ki = kbi; inputs[x * 2 + 1].type = NativeMethods.INPUT_KEYBOARD; } uint success = NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(inputs[0])); ci.DetachFromWindow(); return success == inputs.Length; } return false; }
// 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(typeof(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 == NativeMethods.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 == NativeMethods.WM_KEYUP || skEvent.wm == NativeMethods.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. See DevDiv bugs 23355. 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 ResetKeyboardUsingSendInput(int INPUTSize) { // if the new state is the same, we don't need to do anything if (!(capslockChanged || numlockChanged || scrollLockChanged || kanaChanged)) { return; } // INPUT struct for resetting the keyboard NativeMethods.INPUT[] keyboardInput = new NativeMethods.INPUT[2]; keyboardInput[0].type = NativeMethods.INPUT_KEYBOARD; keyboardInput[0].inputUnion.ki.dwFlags = 0; keyboardInput[1].type = NativeMethods.INPUT_KEYBOARD; keyboardInput[1].inputUnion.ki.dwFlags = NativeMethods.KEYEVENTF_KEYUP; // SendInputs to turn on or off these keys. Inputs are pairs because KeyUp is sent for each one if (capslockChanged) { keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_CAPITAL; UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); } if (numlockChanged) { keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_NUMLOCK; UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); } if (scrollLockChanged) { keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_SCROLL; UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); } if (kanaChanged) { keyboardInput[0].inputUnion.ki.wVk = NativeMethods.VK_KANA; keyboardInput[1].inputUnion.ki.wVk = NativeMethods.VK_KANA; UnsafeNativeMethods.SendInput(2, keyboardInput, INPUTSize); } }
/// <summary> /// Send mouse click to relative position in the client window. /// </summary> /// <param name="client">Target client.</param> /// <param name="button">System.Windows.Forms.MouseButtons to click.</param> /// <param name="x">X position of point to click relative to client window's X position.</param> /// <param name="y">Y position of point to click relative to client window's Y position.</param> /// <param name="doubleClick">True for double click, false for single click.</param> /// <returns>True on success.</returns> public static bool SendMouseClick(int client, MouseButtons button, int x, int y, bool doubleClick) { ClientInfo ci; if (ClientInfoCollection.GetClient(client, out ci)) { NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[2]; NativeMethods.MOUSEINPUT mi = new NativeMethods.MOUSEINPUT(); inputs[0].type = NativeMethods.INPUT_MOUSE; inputs[1].type = NativeMethods.INPUT_MOUSE; mi.dx = 0; mi.dy = 0; switch (button) { case MouseButtons.None: return false; case MouseButtons.Left: mi.dwFlags = NativeMethods.MOUSEEVENTF_LEFTDOWN; inputs[0].mkhi.mi = mi; mi.dwFlags = NativeMethods.MOUSEEVENTF_LEFTUP; inputs[1].mkhi.mi = mi; break; case MouseButtons.Right: mi.dwFlags = NativeMethods.MOUSEEVENTF_RIGHTDOWN; inputs[0].mkhi.mi = mi; mi.dwFlags = NativeMethods.MOUSEEVENTF_RIGHTUP; inputs[1].mkhi.mi = mi; break; case MouseButtons.Middle: mi.dwFlags = NativeMethods.MOUSEEVENTF_MIDDLEDOWN; inputs[0].mkhi.mi = mi; mi.dwFlags = NativeMethods.MOUSEEVENTF_MIDDLEUP; inputs[1].mkhi.mi = mi; break; case MouseButtons.XButton1: mi.mouseData = NativeMethods.XBUTTON1; mi.dwFlags = NativeMethods.MOUSEEVENTF_XDOWN; inputs[0].mkhi.mi = mi; mi.dwFlags = NativeMethods.MOUSEEVENTF_XUP; inputs[1].mkhi.mi = mi; break; case MouseButtons.XButton2: mi.mouseData = NativeMethods.XBUTTON2; mi.dwFlags = NativeMethods.MOUSEEVENTF_XDOWN; inputs[0].mkhi.mi = mi; mi.dwFlags = NativeMethods.MOUSEEVENTF_XUP; inputs[1].mkhi.mi = mi; break; } if (!ci.PrepareWindowForInput()) { ci.DetachFromWindow(); return false; } NativeMethods.WINDOWPLACEMENT wp = new NativeMethods.WINDOWPLACEMENT(); if (NativeMethods.GetWindowPlacement(ci.WindowHandle, ref wp)) { /*int screenX = wp.rcNormalPosition.X + x; int screenY = wp.rcNormalPosition.Y + y; int absX = (screenX * 65536 / Screen.PrimaryScreen.Bounds.Width); int absY = (screenY * 65536 / Screen.PrimaryScreen.Bounds.Height); inputs[0].mkhi.mi.dx = absX; inputs[1].mkhi.mi.dx = absX; inputs[0].mkhi.mi.dy = absY; inputs[1].mkhi.mi.dy = absY;*/ // using this method because absolute position is always off by a pixel or 2 if (!NativeMethods.SetCursorPos(x + wp.rcNormalPosition.X, y + wp.rcNormalPosition.Y)) return false; } else return false; uint success = NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(inputs[0])); if (doubleClick && success == inputs.Length) { success = NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(inputs[0])); } ci.DetachFromWindow(); return success == inputs.Length; } return false; }