/// <summary> /// Inject keyboard input into the system /// </summary> /// <param name="key">indicates the key pressed or released. Can be one of the constants defined in the Key enum</param> /// <param name="press">true to inject a key press, false to inject a key release</param> public static void SendKeyboardInput(Key key, bool press) { InternalUnsafeNativeMethods.INPUT ki = new InternalUnsafeNativeMethods.INPUT(); ki.type = InternalNativeMethods.INPUT_KEYBOARD; ki.union.keyboardInput.wVk = (short)KeyInterop.VirtualKeyFromKey(key); ki.union.keyboardInput.wScan = (short)InternalUnsafeNativeMethods.MapVirtualKey(ki.union.keyboardInput.wVk, 0); int dwFlags = 0; if (ki.union.keyboardInput.wScan > 0) { dwFlags |= InternalNativeMethods.KEYEVENTF_SCANCODE; } if (!press) { dwFlags |= InternalNativeMethods.KEYEVENTF_KEYUP; } ki.union.keyboardInput.dwFlags = dwFlags; if (IsExtendedKey(key)) { ki.union.keyboardInput.dwFlags |= InternalNativeMethods.KEYEVENTF_EXTENDEDKEY; } ki.union.keyboardInput.time = 0; ki.union.keyboardInput.dwExtraInfo = new IntPtr(0); if (InternalUnsafeNativeMethods.SendInput(1, ref ki, Marshal.SizeOf(ki)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } }
//------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #region Public Methods /// <summary> /// Inject pointer input into the system /// </summary> /// <param name="x">x coordinate of pointer, if Move flag specified</param> /// <param name="y">y coordinate of pointer, if Move flag specified</param> /// <param name="data">wheel movement, or mouse X button, depending on flags</param> /// <param name="flags">flags to indicate which type of input occurred - move, button press/release, wheel move, etc.</param> /// <remarks>x, y are in pixels. If Absolute flag used, are relative to desktop origin.</remarks> public static void SendMouseInput(double x, double y, int data, SendMouseInputFlags flags) { int intflags = (int)flags; if ((intflags & (int)SendMouseInputFlags.Absolute) != 0) { int vscreenWidth = InternalUnsafeNativeMethods.GetSystemMetrics(InternalNativeMethods.SM_CXVIRTUALSCREEN); int vscreenHeight = InternalUnsafeNativeMethods.GetSystemMetrics(InternalNativeMethods.SM_CYVIRTUALSCREEN); int vscreenLeft = InternalUnsafeNativeMethods.GetSystemMetrics(InternalNativeMethods.SM_XVIRTUALSCREEN); int vscreenTop = InternalUnsafeNativeMethods.GetSystemMetrics(InternalNativeMethods.SM_YVIRTUALSCREEN); // Absolute input requires that input is in 'normalized' coords - with the entire // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this // first. // // In this normalized world, any pixel on the screen corresponds to a block of values // of normalized coords - eg. on a 1024x768 screen, // y pixel 0 corresponds to range 0 to 85.333, // y pixel 1 corresponds to range 85.333 to 170.666, // y pixel 2 correpsonds to range 170.666 to 256 - and so on. // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. // However, because int math is used, this can end up being rounded into the wrong // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to // push us into the middle of any given pixel's range - that's the 65536/(Width*2) // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably // in the middle of that pixel's block. // The key ting here is that unlike points in coordinate geometry, pixels take up // space, so are often better treated like rectangles - and if you want to target // a particular pixel, target its rectangle's midpoint, not its edge. x = ((x - vscreenLeft) * 65536) / vscreenWidth + 65536 / (vscreenWidth * 2); y = ((y - vscreenTop) * 65536) / vscreenHeight + 65536 / (vscreenHeight * 2); intflags |= InternalNativeMethods.MOUSEEVENTF_VIRTUALDESK; } InternalUnsafeNativeMethods.INPUT mi = new InternalUnsafeNativeMethods.INPUT(); mi.type = InternalNativeMethods.INPUT_MOUSE; mi.union.mouseInput.dx = (int)x; mi.union.mouseInput.dy = (int)y; mi.union.mouseInput.mouseData = data; mi.union.mouseInput.dwFlags = intflags; mi.union.mouseInput.time = 0; mi.union.mouseInput.dwExtraInfo = new IntPtr(0); //Console.WriteLine("Sending"); if (InternalUnsafeNativeMethods.SendInput(1, ref mi, Marshal.SizeOf(mi)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } }
/// <summary> /// Move the mouse to a point and click. The primary mouse button will be used /// this is usually the left button except if the mouse buttons are swaped. /// </summary> /// <param name="pt">The point to click at</param> /// <remarks>pt are in pixels that are relative to desktop origin.</remarks> public static void MoveToAndClick(Point pt) { Input.SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute); // send SendMouseInput works in term of the phisical mouse buttons, therefore we need // to check to see if the mouse buttons are swapped because this method need to use the primary // mouse button. if (InternalUnsafeNativeMethods.GetSystemMetrics(InternalNativeMethods.SM_SWAPBUTTON) == 0) { // the mouse buttons are not swaped the primary is the left Input.SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.LeftDown | SendMouseInputFlags.Absolute); Input.SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.LeftUp | SendMouseInputFlags.Absolute); } else { // the mouse buttons are swaped so click the right button which as actually the primary Input.SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.RightDown | SendMouseInputFlags.Absolute); Input.SendMouseInput(pt.X, pt.Y, 0, SendMouseInputFlags.RightUp | SendMouseInputFlags.Absolute); } }