我正在使用Leap Motion Controller创建一个C# Windows应用程序。我正在Windows 8和Visual Studio 2010上进行开发。我使用user32.dll
中的SetCursorPos
和mouse_event
来移动光标并模拟单击。
我希望在任何应用程序中移动光标。当我从Visual Studio运行/调试它时,它仅在应用程序本身或Visual Studio中工作。在其他应用程序中,鼠标不移动并且单击不起作用,但是如果我尝试使用真正的鼠标移动光标,它会回到它所在的位置。当独立运行时,它不会在Visual Studio中移动,鼠标可以在其他应用程序中使用真正的鼠标移动。
我有这段代码可以使用SetCursorPos
和mouse_event
:
[DllImport("user32.dll")]
public static extern long SetCursorPos(int x, int y);
[DllImport("User32.Dll")]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
public static void MouseClick(uint x, uint y) {
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);
}
在我的表单类中,我将鼠标位置存储在两个字段中。在Timer
的刻度中,我设置光标位置并像这样进行必要的单击:
if (!frame.Hands.IsEmpty) {
Hand hand = frame.Hands.Leftmost;
if (!hand.Fingers.IsEmpty) {
// Get coordinates...
SetCursorPos(mousex, mousey);
}
}
foreach (Gesture gesture : gestures) {
if (gesture.Type == Gesture.GestureType.TYPESCREENTAP) {
MouseClick(mousex, mousey);
}
}
if
语句适用于 Leap 设备;我想移动鼠标并仅在有手获取坐标时才做其他事情。
是否可以移动光标或模拟其他应用程序的鼠标单击?如果是,如何?
你启发我重构我的一些自动化代码:
NativeMethods.cs - 大部分内容来自在线:
using System;
using System.Runtime.InteropServices;
public class NativeMethods
{
[DllImport( "user32.dll", SetLastError = true )]
internal static extern Int32 SendInput( Int32 cInputs, ref INPUT pInputs, Int32 cbSize );
[StructLayout( LayoutKind.Explicit, Pack = 1, Size = 28 )]
internal struct INPUT
{
[FieldOffset( 0 )] public InputType dwType;
[FieldOffset( 4 )] public MOUSEINPUT mi;
[FieldOffset( 4 )] public KEYBDINPUT ki;
[FieldOffset( 4 )] public HARDWAREINPUT hi;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal struct MOUSEINPUT
{
public Int32 dx;
public Int32 dy;
public Int32 mouseData;
public MOUSEEVENTF dwFlags;
public Int32 time;
public IntPtr dwExtraInfo;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal struct KEYBDINPUT
{
public Int16 wVk;
public Int16 wScan;
public KEYEVENTF dwFlags;
public Int32 time;
public IntPtr dwExtraInfo;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal struct HARDWAREINPUT
{
public Int32 uMsg;
public Int16 wParamL;
public Int16 wParamH;
}
internal enum InputType : int
{
Mouse = 0,
Keyboard = 1,
Hardware = 2
}
[Flags()]
internal enum MOUSEEVENTF : int
{
MOVE = 0x1,
LEFTDOWN = 0x2,
LEFTUP = 0x4,
RIGHTDOWN = 0x8,
RIGHTUP = 0x10,
MIDDLEDOWN = 0x20,
MIDDLEUP = 0x40,
XDOWN = 0x80,
XUP = 0x100,
VIRTUALDESK = 0x400,
WHEEL = 0x800,
ABSOLUTE = 0x8000
}
[Flags()]
public enum KEYEVENTF : int
{
EXTENDEDKEY = 1,
KEYUP = 2,
UNICODE = 4,
SCANCODE = 8
}
/// <summary>The MapVirtualKey function translates (maps) a virtual-key code into a scan
/// code or character value, or translates a scan code into a virtual-key code
/// </summary>
/// <param name="uCode">[in] Specifies the virtual-key code or scan code for a key.
/// How this value is interpreted depends on the value of the uMapType parameter</param>
/// <param name="uMapType">[in] Specifies the translation to perform. The value of this
/// parameter depends on the value of the uCode parameter.</param>
/// <returns>Either a scan code, a virtual-key code, or a character value, depending on
/// the value of uCode and uMapType. If there is no translation, the return value is zero</returns>
/// <remarks></remarks>
[DllImport( "User32.dll", SetLastError = false, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto )]
public static extern UInt32 MapVirtualKey( UInt32 uCode, MapVirtualKeyMapTypes uMapType );
/// <summary>The set of valid MapTypes used in MapVirtualKey
/// </summary>
/// <remarks></remarks>
public enum MapVirtualKeyMapTypes : uint
{
/// <summary>uCode is a virtual-key code and is translated into a scan code.
/// If it is a virtual-key code that does not distinguish between left- and
/// right-hand keys, the left-hand scan code is returned.
/// If there is no translation, the function returns 0.
/// </summary>
/// <remarks></remarks>
MAPVK_VK_TO_VSC = 0x0,
/// <summary>uCode is a scan code and is translated into a virtual-key code that
/// does not distinguish between left- and right-hand keys. If there is no
/// translation, the function returns 0.
/// </summary>
/// <remarks></remarks>
MAPVK_VSC_TO_VK = 0x1,
/// <summary>uCode is a virtual-key code and is translated into an unshifted
/// character value in the low-order word of the return value. Dead keys (diacritics)
/// are indicated by setting the top bit of the return value. If there is no
/// translation, the function returns 0.
/// </summary>
/// <remarks></remarks>
MAPVK_VK_TO_CHAR = 0x2,
/// <summary>Windows NT/2000/XP: uCode is a scan code and is translated into a
/// virtual-key code that distinguishes between left- and right-hand keys. If
/// there is no translation, the function returns 0.
/// </summary>
/// <remarks></remarks>
MAPVK_VSC_TO_VK_EX = 0x3,
/// <summary>Not currently documented
/// </summary>
/// <remarks></remarks>
MAPVK_VK_TO_VSC_EX = 0x4
}
}
鼠标输入.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace SendInput
{
public class MouseInput
{
public static void LeftClick()
{
DoMouse( NativeMethods.MOUSEEVENTF.LEFTDOWN, new System.Drawing.Point( 0, 0 ) );
DoMouse( NativeMethods.MOUSEEVENTF.LEFTUP, new System.Drawing.Point( 0, 0 ) );
}
public static void LeftClick( int x, int y )
{
DoMouse( NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE, new System.Drawing.Point( x, y ) );
DoMouse( NativeMethods.MOUSEEVENTF.LEFTDOWN, new System.Drawing.Point( x, y ) );
DoMouse( NativeMethods.MOUSEEVENTF.LEFTUP, new System.Drawing.Point( x, y ) );
}
public static void ClickBoundingRectangleByPercentage( int xPercentage, int yPercentage, System.Drawing.Rectangle bounds )
{
double additional = 0.0;
if ( xPercentage == 99 )
additional = 0.5;
int xPixel = Convert.ToInt32( bounds.Left + bounds.Width * ( xPercentage + additional ) / 100 );
int yPixel = Convert.ToInt32( bounds.Top + bounds.Height * ( yPercentage ) / 100 );
LeftClick( xPixel, yPixel );
}
public static void RightClick()
{
DoMouse( NativeMethods.MOUSEEVENTF.RIGHTDOWN, new System.Drawing.Point( 0, 0 ) );
DoMouse( NativeMethods.MOUSEEVENTF.RIGHTUP, new System.Drawing.Point( 0, 0 ) );
}
public static void RightClick( int x, int y )
{
DoMouse( NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE, new System.Drawing.Point( x, y ) );
DoMouse( NativeMethods.MOUSEEVENTF.RIGHTDOWN, new System.Drawing.Point( x, y ) );
DoMouse( NativeMethods.MOUSEEVENTF.RIGHTUP, new System.Drawing.Point( x, y ) );
}
public static void MoveMouse( Point p )
{
MoveMouse( p.X, p.Y );
}
public static void MoveMouse( System.Windows.Point p )
{
MoveMouse( Convert.ToInt32( p.X ), Convert.ToInt32( p.Y ) );
}
public static void MoveMouse( int x, int y )
{
DoMouse( NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE, new System.Drawing.Point( x, y ) );
}
public static System.Drawing.Point GetMousePosition()
{
return Cursor.Position;
}
public static void ScrollWheel( int scrollSize )
{
DoMouse( NativeMethods.MOUSEEVENTF.WHEEL, new System.Drawing.Point( 0, 0 ), scrollSize );
}
private static void DoMouse( NativeMethods.MOUSEEVENTF flags, Point newPoint, int scrollSize = 0 )
{
NativeMethods.INPUT input = new NativeMethods.INPUT();
NativeMethods.MOUSEINPUT mi = new NativeMethods.MOUSEINPUT();
input.dwType = NativeMethods.InputType.Mouse;
input.mi = mi;
input.mi.dwExtraInfo = IntPtr.Zero;
// mouse co-ords: top left is (0,0), bottom right is (65535, 65535)
// convert screen co-ord to mouse co-ords...
input.mi.dx = newPoint.X * 65535 / Screen.PrimaryScreen.Bounds.Width;
input.mi.dy = newPoint.Y * 65535 / Screen.PrimaryScreen.Bounds.Height;
input.mi.time = 0;
input.mi.mouseData = scrollSize * 120;
// can be used for WHEEL event see msdn
input.mi.dwFlags = flags;
int cbSize = Marshal.SizeOf( typeof ( NativeMethods.INPUT ) );
int result = NativeMethods.SendInput( 1, ref input, cbSize );
if ( result == 0 )
Debug.WriteLine( Marshal.GetLastWin32Error() );
}
}
}
是的,你想使用"SendInput"功能。
请参阅: 除非我移动光标,否则发送输入不执行单击鼠标按钮
也
http://www.pinvoke.net/default.aspx/user32.sendinput
和
http://www.pinvoke.net/default.aspx/Structures/INPUT.html
我已经修复了第一个超链接 - 请参阅有关以下内容的评论:
使用 SendInput 函数时,应考虑一些事项。
如果未指定MOUSEEVENTF_ABSOLUTE标志,则 dx 和 dy(鼠标输入数据结构)是当前鼠标位置的相对坐标。如果指定MOUSEEVENTF_ABSOLUTE则 dx 和 dy 是介于 0 和 65535 之间的绝对坐标。因此,如果您的 x 和 y 坐标是屏幕坐标,则应使用以下函数来计算 dx 和 dy:
和
http://msdn.microsoft.com/en-us/library/ms646310%28VS.85%29.aspx
我设法自己解决了这个问题。
有两个问题。第一个(也是主要的)问题是 Leap 设备在后台时没有向我的应用程序发送帧。已通过将此代码添加到侦听器的 onConnect()
方法中修复此问题,如下所示:
controller.SetPolicyFlags(Controller.PolicyFlag.POLICYBACKGROUNDFRAMES);
第二个问题是,当独立运行时,应用程序无权向其他应用程序发送输入。我按照此处的说明(向下滚动,有关于uiAccess
的信息)和此处并签署了我的应用程序并将其添加到清单中:
<requestedExecutionLevel level="asInvoker" uiAccess="true" />