C#-使用Win32_API的SendMessage实现指定窗口的模拟点击操作

Posted 青丝·旅人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#-使用Win32_API的SendMessage实现指定窗口的模拟点击操作相关的知识,希望对你有一定的参考价值。

这些天因为需求原因,要实现指定窗口的点击操作。因为是萌新,所以我只能在度娘的帮助下,一步步实现这个简单的功能。(使用API方法需引入System.Runtime.InteropServices命名空间)
(注:该情景我已获取了我需要操作窗口的句柄,如要自行获取请使用Win32API下的FindWindow方法与FindWindowEx方法)

1.过程中绕了许多弯路
最开始是想通过Win32API下的mouse_event来实现鼠标的模拟点击的操作,但用此方法需要先用SetForegroudWindow方法将窗口置顶,然后再使用mouse_event方法来进行点击;这种操作并不是我理想中的效果。
这种方式是通过模拟前台鼠标点击,先将所需操作的窗口置前,然后通过鼠标事件模拟点击操作进行点击。很遗憾,我使用这种方法并没有执行成功,后来我了解到这种方法需要配合几个其他的API方法使用。

        using System.Runtime.InteropServices;
 
        private static int MOUSEEVENTF_MOVE = 0x0001;      //移动鼠标 
        private static int MOUSEEVENTF_LEFTDOWN = 0x0002; //模拟鼠标左键按下 
        private static int MOUSEEVENTF_LEFTUP = 0x0004; //模拟鼠标左键抬起 
        private static int MOUSEEVENTF_RIGHTDOWN = 0x0008; //模拟鼠标右键按下 
        private static int MOUSEEVENTF_RIGHTUP = 0x0010; //模拟鼠标右键抬起 
        private static int MOUSEEVENTF_MIDDLEDOWN = 0x0020; //模拟鼠标中键按下 
        private static int MOUSEEVENTF_MIDDLEUP = 0x0040; //模拟鼠标中键抬起 
        private static int MOUSEEVENTF_ABSOLUTE = 0x8000; //标示是否采用绝对坐标 
 
        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);//指定句柄窗口置前
 
        [System.Runtime.InteropServices.DllImport("user32")]
        public static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);//https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event  有参数详解
 
        SetForegroundWindow(new IntPtr(窗口句柄));
        mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);    
View Code

2.然后我便又接触到SetCursorPos这个API方法、并在大佬博客知识海领悟出了MoveMouseToPoint、SetMouseRectangle、SetMouseAtCenterScreen等方法,继而再次挑战我的目标。
原理是先通过SetCursorPos设置鼠标光标的位置,然后通过MoveMouseToPoint移动鼠标到指定位置,再通过mouse_event进行点击
其实这个点击方式与第一种方法大同小异,无非是多了一个设置鼠标位置的过程,虽然这此完成了我所要的点击效果,却没有达到我的真正的目的。

using System.Runtime.InteropServices;

public class MouseClick
{
        /// <summary>
        /// 引用user32.dll动态链接库(windows api),
        /// 使用库中定义 API:SetCursorPos 
        /// </summary>
        [DllImport("user32.dll")]
        private static extern int SetCursorPos(int x, int y);

        /// <summary>
        /// 移动鼠标到指定的坐标点
        /// </summary>
        /// <param name="p">移动的点位</param>
        public void MoveMouseToPoint(Point p)
        {
            SetCursorPos(p.X, p.Y);
        }

        /// <summary>
        /// 设置鼠标的移动范围
        /// </summary>
        /// <param name="rectangle">移动范围的矩阵</param>
        public void SetMouseRectangle(Rectangle rectangle)
        {
             System.Windows.Forms.Cursor.Clip = rectangle;
        }

        /// <summary>
        /// 中心坐标x或y
        /// </summary>
        public static int loginx, loginy;
        /// <summary>
        /// 设置鼠标位于屏幕中心
        /// </summary>
        public void SetMouseAtCenterScreen()
        {
            //当前屏幕的宽高
            int winHeight = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
            int winWidth = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
            //设置鼠标的x,y位置
            loginx = winWidth / 3 * 2;
            loginy = winHeight / 4 + 5;
            Point centerP = new Point(loginx, loginy);
            //移动鼠标
            MoveMouseToPoint(centerP);
        }

        public const int MOUSEEVENTF_LEFTDOWN = 0x2;//左键按下
        public const int MOUSEEVENTF_LEFTUP = 0x4;//左键弹起
        public enum MouseEventFlags
        {
            Move = 0x0001, //移动鼠标
            LeftDown = 0x0002,//模拟鼠标左键按下
            LeftUp = 0x0004,//模拟鼠标左键抬起
            RightDown = 0x0008,//鼠标右键按下
            RightUp = 0x0010,//鼠标右键抬起
            MiddleDown = 0x0020,//鼠标中键按下 
            MiddleUp = 0x0040,//中键抬起
            Wheel = 0x0800,
            Absolute = 0x8000//标示是否采用绝对坐标
        }
        /// <summary>
        /// 鼠标事件
        /// </summary>
        /// <param name="dwFlags">鼠标操作指令</param>
        /// <param name="dx">操作点x</param>
        /// <param name="dy">操作点y</param>
        /// <param name="cButtons">参数1</param>
        /// <param name="dwExtraInfo">参数2</param>
        [DllImport("User32")]
        public extern static void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

        /// <summary>
        /// 鼠标左键点击屏幕中心
        /// </summary>
        public void MouseClick_CenterScree()
        {
            SetMouseAtCenterScreen();
            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, loginx, loginy, 0, 0);
            //mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, loginx, loginy, 0, 0);//点击第二次
        }
}
View Code

3.经过这两此的资料捞取代码实验,我发现mouse_event是不能达到我想要的效果的,于是我便把目光投向了一个新的API方法,那便是SendMessage(windows系统信息交互方法)。
SendMessage的执行原理是:将执行的操作消息发送到一个或多个窗口,函数方法调用指定窗口的窗口过程,执行窗口过程处理完成该操作消息,最后执行返回。
该函数方法原型是↓
LRESULT SendMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
属于User32.dll中的函数方法。具体参数详解请参照:SendMessage参数详解
刚接触这个函数方法,也没有立刻解决指定模拟点击的问题;因为SendMessage用法有许多,光是参数Msg就包含了许多种。SendMessage发送消息类型(Msg)
最后在不断的度娘帮助下,完成了我想要的效果
(注:因为在此消息类型中,坐标y的值属于高位,所以要右移16位变为高位数)

using System.Runtime.InteropServices

public class Mouer_Click
{
        [DllImport("user32.dll", EntryPoint = "SendMessageA")]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        SendMessage(new IntPtr(窗口句柄), 0x202, IntPtr.Zero, new IntPtr(x + (y << 16)));
}
View Code

 

虽然过程非常的坎坷,但是最终还是实现了我想要的效果。非常开心与大家分享此次模拟点击的经验,希望学C#大家能在这篇随笔的帮助下,不像我一样,少走一些弯路,感谢您观赏!

以上是关于C#-使用Win32_API的SendMessage实现指定窗口的模拟点击操作的主要内容,如果未能解决你的问题,请参考以下文章

win32api 找不到指定的模块

如何从 Win32 API 中的总线关系中获取设备实例路径

使用 Win32 API 的 Windows“真实”用户列表

如何使用 WIN32 C/C++ API 告诉 Windows 10 平铺、居中或拉伸桌面壁纸?

使用 Win32/C++ API 更改 Windows 7 壁纸(操作未实现异常)

Win32 API 中的 ReadFile 函数