110,534
社区成员
发帖
与我相关
我的任务
分享
问题:我在做一款自动化测试工具(这不是重点),以登录为例,其中,会先从文本中读取一些数据(比如:账号、密码),试图先将账号设置到剪切板,然后模拟鼠标点击账号输入框,再模拟键盘发送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的问题,总归折腾了一大圈,还是没能解决。实在是没想法了,请大神施以援手了。
WM_CLEAR
WM_COPY
WM_CUT
WM_PASTE
试试发消息
你往哪里粘贴,这是重点
看不出啥问题。不过通常来说操作 Clipboard是winform天生具备的能力,所以你可以直接写
Clipboard.SetText();
Clipboard类本身就是System.Windows.Forms 自己带的工具类
顶一下,在线等。。。
可能密码框故意限制了粘贴密码