模拟鼠标点击
模拟鼠标点击大概率跟键盘输入一样,某些软件会检测 物理
将实时鼠标坐标直接显示在窗口标题栏,既能简化界面(无需额外文本框),又能直观看到坐标,以下是优化后的完整代码,核心改动是把实时X/Y坐标更新到Form的Text属性,同时保留复制坐标、输入坐标模拟点击的核心功能。
一、优化后代码(简化界面+标题栏显示坐标)
步骤1:WinForm界面控件(极简版)
| 控件类型 | 名称 | 文本/用途 |
|---|---|---|
| TextBox | txtTargetX | 输入X坐标 |
| TextBox | txtTargetY | 输入Y坐标 |
| Button | btnSimulateClick | 模拟点击 |
(无需再添加显示实时坐标的文本框,所有实时坐标直接显示在窗口标题)
步骤2:核心代码(Form1.cs)
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MouseClickSimulator
{
public partial class Form1 : Form
{
// ====================== Windows API声明 ======================
[DllImport("user32.dll")]
private static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(out POINT lpPoint);
// 鼠标事件常量
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
// 点结构体
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
// 存储当前鼠标坐标(方便复制)
private int _currentX;
private int _currentY;
// ====================== 窗体初始化 ======================
public Form1()
{
InitializeComponent();
// 初始化窗口标题
this.Text = "鼠标坐标模拟器 - 当前坐标:X=0, Y=0";
// 定时器轮询获取全局鼠标坐标(50ms刷新一次)
Timer timer = new Timer
{
Interval = 50
};
timer.Tick += Timer_Tick;
timer.Start();
}
// ====================== 核心:标题栏实时显示坐标 ======================
private void Timer_Tick(object sender, EventArgs e)
{
if (GetCursorPos(out POINT currentPos))
{
// 更新全局变量(用于复制)
_currentX = currentPos.X;
_currentY = currentPos.Y;
// 更新窗口标题(核心改动)
this.Text = $"鼠标坐标模拟器 - 当前坐标:X={_currentX}, Y={_currentY}";
}
}
// ====================== 模拟坐标点击 ======================
private void btnSimulateClick_Click(object sender, EventArgs e)
{
// 验证输入
if (!int.TryParse(txtTargetX.Text.Trim(), out int targetX) ||
!int.TryParse(txtTargetY.Text.Trim(), out int targetY))
{
MessageBox.Show("请输入有效的数字坐标!");
return;
}
try
{
// 移动鼠标+模拟点击
SetCursorPos(targetX, targetY);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, targetX, targetY, 0, 0);
MessageBox.Show($"已模拟点击:X={targetX}, Y={targetY}");
}
catch (Exception ex)
{
MessageBox.Show($"模拟点击失败:{ex.Message}\n请以管理员身份运行!");
}
}
}
}
二、核心优化点说明
标题栏实时显示坐标
- 直接通过
this.Text = $"XXX{_currentX},{_currentY}"更新窗口标题。 - 保留
_currentX/_currentY全局变量,用于复制坐标和后续扩展。
- 直接通过
界面极简化
- 仅保留必要控件: 目标坐标输入框、模拟点击按钮,界面更清爽。
三、使用效果
- 运行程序后,窗口标题会实时刷新:
鼠标坐标模拟器 - 当前坐标:X=156, Y=389 - 移动鼠标时,标题栏的X/Y数值会立即更新(50ms刷新一次,肉眼无延迟)。
- 将复制的坐标粘贴到目标坐标输入框(拆分X/Y),点击「模拟点击」即可触发操作。
四、额外优化(可选)
1. 标题栏格式自定义
若想让标题更简洁,可修改标题格式:
// 极简格式(仅显示坐标)
this.Text = $"X:{_currentX} Y:{_currentY}";
// 带备注格式
this.Text = $"坐标模拟器 | X={_currentX} Y={_currentY}";
2. 高DPI适配
若屏幕缩放导致坐标偏移,可在窗体初始化时添加DPI适配:
public Form1()
{
InitializeComponent();
// 禁用DPI缩放(避免坐标偏移)
this.AutoScaleMode = AutoScaleMode.None;
// 其他初始化代码...
}
3. 延迟点击(实用扩展)
添加一个NumericUpDown控件(命名nudDelay),实现延迟点击(方便切换到目标窗口):
private void btnSimulateClick_Click(object sender, EventArgs e)
{
if (!int.TryParse(txtTargetX.Text.Trim(), out int targetX) ||
!int.TryParse(txtTargetY.Text.Trim(), out int targetY))
{
MessageBox.Show("请输入有效的数字坐标!");
return;
}
// 获取延迟时间(默认0秒)
int delayMs = (int)nudDelay.Value * 1000;
// 延迟执行点击
Task.Delay(delayMs).ContinueWith(t =>
{
try
{
SetCursorPos(targetX, targetY);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, targetX, targetY, 0, 0);
this.Invoke(new Action(() =>
MessageBox.Show($"延迟{delayMs/1000}秒后点击:X={targetX}, Y={targetY}")
));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
MessageBox.Show($"点击失败:{ex.Message}")
));
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
实现鼠标双击
只需在单击基础上补充一次 “按下 + 抬起” 的操作(或直接用双击专用常量)
一、核心原理(双击实现)
Windows 系统中,双击本质是两次快速的单击(间隔约 500ms 内),有两种实现方式:
- 简易版:连续调用两次单击事件(最通用,兼容所有系统);
- 原生版:使用
MOUSEEVENTF_LEFTDBLCLK常量(直接触发双击,部分控件更适配)。
| RadioButton | rdoDoubleClick | 双击 |
|---|
// 鼠标事件常量
private const int MOUSEEVENTF_LEFTDOWN = 0x02; // 左键按下
private const int MOUSEEVENTF_LEFTUP = 0x04; // 左键抬起
private const int MOUSEEVENTF_LEFTDBLCLK = 0x08; // 左键双击(原生常量)
// 方式1:原生双击(推荐,部分控件更适配)
mouse_event(MOUSEEVENTF_LEFTDBLCLK, targetX, targetY, 0, 0);
// 方式2:连续两次单击(兼容所有场景,可替代上面一行)
// mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, targetX, targetY, 0, 0);
// System.Threading.Thread.Sleep(50); // 双击间隔(50ms,可调整)
// mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, targetX, targetY, 0, 0);
1. 两种双击实现方式对比
| 方式 | 代码示例 | 适用场景 |
|---|---|---|
| 原生双击(推荐) | mouse_event(MOUSEEVENTF_LEFTDBLCLK, X, Y, 0, 0) |
系统原生控件(如文件夹、按钮),更贴合实际操作 |
| 连续单击(兼容) | 两次LEFTDOWN+LEFTUP + 50ms 延迟 |
自定义控件 / 游戏等不识别原生双击的场 |
2. 双击间隔调整
若原生双击失效,改用 “连续单击” 时,可调整Thread.Sleep(50)的数值(范围 50-200ms),匹配系统双击间隔(Windows 默认双击间隔是 500ms,可在「控制面板→鼠标」中查看)。
3. 高兼容性替代方案(SendInput)
若mouse_event在新版 Windows 中失效,可替换为SendInput实现双击(更稳定):
// 补充SendInput相关API
[StructLayout(LayoutKind.Sequential)]
private struct INPUT { public int type; public MOUSEINPUT mi; }
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT { public int dx, dy, mouseData, dwFlags, time; public IntPtr dwExtraInfo; }
[DllImport("user32.dll")]
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
// 模拟双击(SendInput版)
private void SimulateDoubleClick(int x, int y)
{
// 坐标归一化(适配多屏幕)
int dx = (x * 65535) / Screen.PrimaryScreen.Bounds.Width;
int dy = (y * 65535) / Screen.PrimaryScreen.Bounds.Height;
INPUT[] inputs = new INPUT[4];
// 移动鼠标
inputs[0].type = 0;
inputs[0].mi.dx = dx;
inputs[0].mi.dy = dy;
inputs[0].mi.dwFlags = 0x8000 | 0x0001; // MOVE + VIRTUALDESK
// 第一次单击(按下+抬起)
inputs[1].type = 0;
inputs[1].mi.dwFlags = 0x0002; // LEFTDOWN
inputs[2].type = 0;
inputs[2].mi.dwFlags = 0x0004; // LEFTUP
// 第二次单击(按下+抬起,双击核心)
inputs[3].type = 0;
inputs[3].mi.dwFlags = 0x0002 | 0x0004; // LEFTDOWN + LEFTUP
SendInput(4, inputs, Marshal.SizeOf(typeof(INPUT)));
}
快捷键触发:添加 F5(单击)、F6(双击)快捷键,无需点击按钮:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.F5) { btnSimulateClick.PerformClick(); return true; }
if (keyData == Keys.F6) { rdoDoubleClick.Checked = true; btnSimulateClick.PerformClick(); return true; }
return base.ProcessCmdKey(ref msg, keyData);
}