关于C#调用WinApi实现剪切板操作及模拟键盘Ctrl+V的问题,请大神指点、小神围观
问题:我在做一款自动化测试工具(这不是重点),以登录为例,其中,会先从文本中读取一些数据(比如:账号、密码),试图先将账号设置到剪切板,然后模拟鼠标点击账号输入框,再模拟键盘发送Ctrl+V,密码的输入过程也一样。输入账号的过程OK,问题是输入密码的时候,粘贴的数据依然是账号数据。
尝试:
1、在模拟键盘发送Ctrl+V粘贴密码前,先从剪切板读取数据并输出,发现确实是密码数据,但模拟键盘发送Ctrl+V后,文本框中缺还是账号数据。
2、在模拟键盘发送Ctrl+V粘贴密码的位置打断点,在记事本中按Ctrl+V,发现也是正确的密码数据。
很迷惑究竟是哪里出了问题。
核心代码如下:
public static partial class WinApi
{
public static void SendCtrlV(IntPtr hWnd)
{
SetForegroundWindow(hWnd);
System.Threading.Thread.Sleep(10);
keybd_event(VK.VK_CONTROL, 0, 0x00, 0); //按下ctrl,在下面释放之前,他的状态一直还是被按下的,不信你试下找个地方按 v
System.Threading.Thread.Sleep(10);
keybd_event(VK.VK_V, 0, 0x00, 0);
System.Threading.Thread.Sleep(10);
keybd_event(VK.VK_V, 0, 0x02, 0);
System.Threading.Thread.Sleep(10);
keybd_event(VK.VK_CONTROL, 0, 0x02, 0); //释放 ctrl 键
}
public static class ClipboardFormat
{
// 当然这里还有其他的值,省略了……
/// <summary>
/// Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character
/// signals the end of the data.
/// </summary>
public const int CF_UNICODETEXT = 13;
}
[DllImport("User32", EntryPoint = "OpenClipboard", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("User32", EntryPoint = "CloseClipboard", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern bool CloseClipboard();
[DllImport("User32", EntryPoint = "EmptyClipboard", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern bool EmptyClipboard();
[DllImport("User32", EntryPoint = "IsClipboardFormatAvailable", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern bool IsClipboardFormatAvailable(int format);
[DllImport("User32", EntryPoint = "GetClipboardData", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr GetClipboardData(int uFormat);
[DllImport("User32", EntryPoint = "SetClipboardData", ExactSpelling = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetClipboardData(int uFormat, IntPtr hMem);
}
public partial class WinCommon
{
public static void OpenClipboard(IntPtr owner)
{
while (!WinApi.OpenClipboard(owner)) { System.Threading.Thread.Sleep(10); }
}
public static void CloseClipboard()
{
while (!WinApi.CloseClipboard()) { System.Threading.Thread.Sleep(10); }
}
public static void ClearClipboard()
{
while (!WinApi.EmptyClipboard()) { System.Threading.Thread.Sleep(10); }
}
public static void SetTextToClipboard(string data)
{
Trace.WriteLine("WinCommon : SetTextToClipboard : "
+ WinApi.SetClipboardData(WinApi.ClipboardFormat.CF_UNICODETEXT, Marshal.StringToHGlobalUni(data)).ToString());
}
public static string GetTextFromClipboard()
{
string value = string.Empty;
if (WinApi.IsClipboardFormatAvailable(WinApi.ClipboardFormat.CF_UNICODETEXT))
{
IntPtr ptr = WinApi.GetClipboardData(WinApi.ClipboardFormat.CF_UNICODETEXT);
if (ptr != IntPtr.Zero)
{
value = Marshal.PtrToStringUni(ptr);
}
}
return value;
}
}
// 以下是业务处理部分的代码片断
switch (it)
{
case InputTypes.Account:
if (null != LogMessage) LogMessage($"输入账号'{accounts[currentAccIndex].Id}'。");
MouseDoubleClick(new Point(30, points.FirstEntity.Y));
Thread.Sleep(1000);
WinCommon.OpenClipboard(IntPtr.Zero);
WinCommon.ClearClipboard();
WinCommon.SetTextToClipboard(accounts[currentAccIndex].Id);
WinCommon.CloseClipboard();
WinCommon.OpenClipboard(winRenderHandle);
Trace.WriteLine("LoginProcessor:Process:InputTypes.Account: " + WinCommon.GetTextFromClipboard());
WinCommon.CloseClipboard();
Thread.Sleep(1000);
WinApi.SendCtrlV(winRenderHandle); // 这里粘贴的数据还OK
Thread.Sleep(1000);
//MouseClick(new Point(30, points.FirstEntity.Y - 100));
MouseClick(points.FirstEntity);
//Thread.Sleep(1500);
it = InputTypes.Password;
waitForPageChange = true;
break;
case InputTypes.Password:
if (null != LogMessage) LogMessage($"输入密码'{accounts[currentAccIndex].Pswd}'。");
MouseDoubleClick(new Point(30, points.FirstEntity.Y));
Thread.Sleep(1000);
WinCommon.OpenClipboard(IntPtr.Zero);
WinCommon.ClearClipboard();
WinCommon.SetTextToClipboard(accounts[currentAccIndex].Pswd);
WinCommon.CloseClipboard();
WinCommon.OpenClipboard(winRenderHandle);
Trace.WriteLine("LoginProcessor:Process:InputTypes.Password: " + WinCommon.GetTextFromClipboard());
WinCommon.CloseClipboard();
Thread.Sleep(1000);
WinApi.SendCtrlV(winRenderHandle); // 就是这里粘贴的数据不符合预期
Thread.Sleep(1000);
//MouseClick(new Point(30, points.FirstEntity.Y - 100));
MouseClick(points.FirstEntity);
//Thread.Sleep(1500);
it = InputTypes.Finished;
waitForPageChange = true;
break;
default:
break;
}
从直接使用C#带的Clipboard到直接调用WinApi,中间还碰到STA的问题,总归折腾了一大圈,还是没能解决。实在是没想法了,请大神施以援手了。
相关推荐
《模式识别与数据挖掘》配套PPT课件
《模式识别与数据挖掘》配套PPT课件
科技成果转化项目进场交易前,评估备案环节需要准备哪些材料?_8.docx
科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。
人工智能通识教育(中职版)课程标准+PPT课件+部分素材+电子活页+在线考核
人工智能通识教育(中职版)课程标准+PPT课件+部分素材+电子活页+在线考核
跨境电商供应链管理(AIGC版)课件+教案+参考答案
跨境电商供应链管理(AIGC版)课件+教案+参考答案
110KV单电源电网三段式电流保护+距离继电保护整定计算说明报告(23页+6000字)
内容概要:本文是一份关于110kV单电源电网继电保护整定计算的综合性技术报告,全文共23页、约6000字,系统阐述了三段式电流保护与距离保护的设计原理及其整定计算方法。报告深入分析了电力系统在短路故障条件下的电流与阻抗特性,详细推导了电流保护Ⅰ、Ⅱ、Ⅲ段的动作范围、整定原则、灵敏度校验及时间配合机制,确保满足继电保护的选择性、速动性、灵敏性和可靠性“四性”要求。同时,针对长距离或重要输电线路,补充介绍了距离保护的阻抗整定计算方法,并可能结合Matlab进行仿真验证与辅助计算,提升整定结果的准确性与工程实用性。; 适合人群:适用于电气工程及其自动化相关专业的高年级本科生、研究生,以及从事电力系统继电保护设计、运行维护的工程技术人员;要求读者具备电力系统分析、短路计算及继电保护的基础理论知识。; 使用场景及目标:①掌握110kV单电源辐射型电网中三段式电流保护的整定流程与实际计算步骤;②理解距离保护的工作原理及其在复杂工况下的应用优势;③完成实际工程项目的保护配置与整定计算任务,或作为课程设计、毕业设计及科研课题的技术参考资料;④结合Matlab工具实现整定计算的程序化处理与动作特性仿真验证。; 阅读建议:建议读者结合《电力系统继电保护原理》等相关教材同步学习,重点研读整定公式的推导过程与典型算例的计算细节,注意区分电流保护与距离保护的适用场景与配置逻辑,有条件时应利用Matlab等工具对保护动作行为进行建模与仿真,以深化对保护协调配合机制的理解。


