自动单击 HTML 弹出窗口中的按钮





4.00/5 (4投票s)
本文提供了一种实用的解决方案,用于点击弹出窗口上的按钮。
引言
你是否有一个监控 HTML 页面的进程?你是否正在编写一个测试工具?你是否偶尔会遇到一个弹出窗口,导致你的进程停止?如果是这样,那么本文希望能为你提供一种自动点击弹出对话框上的按钮的方法。
背景
我正在为工作编写一个测试工具,以测试一些旧的 Classic ASP 页面。是的,我知道有很多测试框架,但我有一些非常具体的要求,并且我一直遇到一个又一个问题。我创建了一个 hack 测试项目来识别问题并找到解决方案。在这样做的时候,我发现我有一个问题,即自动点击一个弹出按钮。在 Google 上搜索了一段时间后,我发现其他人在使用 WPF WebBrowser
时也遇到了同样的问题,并且没有可用的解决方案。所以,我在 Microsoft WPF 论坛上发帖,寻求关于 WebBrowser
控件的帮助。
Title: WebBrowser - How to click button on javascript confirm popup
I'm writing a test harness for an older classic ASP application that we have.
Some of the pages have JavaScript confirm dialog boxes like the following
var answer = confirm('Are you sure you want to delete the record.\n\nOK – proceed with delete Cancel – no change');
I would like to be able to click on the "OK" or "Cancel" button from my C# test harness application if possible.
This will allow for the rest of the code to be tested.
Any thoughts on how to fix this problem is appreciated and no I can't rewrite the classic ASP application.
在几个小时内,论坛管理员回复说
Classic ASP application is a very old product which is out of the support scope of this forum.
We will move this thread to Off-topic forum. Thank you for your understanding.
坦率地说,我不明白 HTML 页面是如何呈现的与使 WPF WebBrowser
控件工作有什么关系。
我再次开始了 Google 搜索。我发现有人发表了一篇晦涩的帖子,说也许你可以获得窗口的句柄并做一些事情。万一“获取窗口的句柄”对你来说没有任何意义,那么你将会得到惊喜,P/Invoke。本文不会描述 P/Invoke,因为它本身就是一个主题,而是为你提供一个使用 P/Invoke 方法点击按钮的工作示例。
一个包含 P/Invoke 良好信息的网站是 PINVOKE .NET
使用代码
以下类是解决方案的核心。包含的示例项目有一些额外的类,使实现更容易一些。
这个类将通过标题找到一个窗口。这不一定意味着 JavaScript 或 VBScript 窗口。它可以用来查找 NotePad.exe 的运行实例,但必须进行修改才能使用。有更好的方法来访问正在运行的应用程序,并且由于可执行文件不在本文的范围内,我将把本文的其余部分限制在 HTML 脚本语言上。
在 SearchForWindow
方法中,你将看到 FindWindow
和 FindWindowByCaption
的代码。它们基本上是同一件事。使用任何一种方法都可以。
如果你的要求规定你必须找到弹出窗口上的特定文本,你将不得不寻找其他 P/Invoke 选项来实现这一点。
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace AutoClickPopup
{
public class Popup
{
#region Constants
const int BM_CLICK = 0x00F5;
#endregion
#region P/Invoke Methods
// Example: FindWindow((string)null, windowName)
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// You must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
// Use to find controls on the popup window
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr parentHandle,
IntPtr childAfter, string className, string windowTitle);
// Send a Message to a control
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
#endregion
private readonly string _windowTitle;
private IntPtr _window;
/// <summary />
///
/// </summary />
/// <param name="windowTitle" /></param />
public Popup(string windowTitle)
{
_windowTitle = windowTitle;
}
/// <summary />
/// Indicates if the windows was successfully found and is usable
/// </summary />
public bool IsFound { get { return _window != IntPtr.Zero; } }
/// <summary />
/// Try and find the window by the window title
/// </summary />
/// <returns />If the window was found</returns />
public bool SearchForWindow()
{
try
{
_window = FindWindow(null, _windowTitle);
//_window = FindWindowByCaption(IntPtr.Zero, _windowTitle);
}
catch
{
// Ignore Exceptions
}
return IsFound;
}
/// <summary />
/// Click a button on the windows
/// </summary />
/// <param name="buttonText" />Text of the button</param />
public void ClickButton(string buttonText)
{
try
{
IntPtr btn = FindWindowEx(_window, IntPtr.Zero, "Button", buttonText);
if (btn != IntPtr.Zero)
{
IntPtr rc = SendMessage(btn, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
BreakOnError(rc);
}
}
catch
{
// Ignore Exceptions
}
}
[Conditional("DEBUG")]
public void BreakOnError(IntPtr code)
{
if (code != IntPtr.Zero)
Debugger.Break();
}
}
}
需要注意的一点是,这段代码必须在另一个线程上运行。你可能会问...为什么?因为,一旦脚本窗口弹出,UI 线程就会阻塞,直到按钮被按下,这有点违背了这个类的目的。所以,确保你在另一个线程上。
最后,示例应用程序被设置为允许弹出窗口显示一段时间(5 秒),然后才点击按钮。在使用你项目中的代码时,你会想要修复这个问题。请参阅 ClickButtonTask
构造函数中的注释。
运行示例应用程序
编译并启动应用程序。它将启动应用程序并加载一个示例 HTML 页面。如果你看到下面显示的安全横幅,你将必须点击“允许阻止的内容...”选项。如果你不这样做,JavaScript 和 VB Script 将不会运行。在那个安全提示之后,你还会收到另一个提示,询问你是否确定...点击“是”。
要进行测试,请点击屏幕顶部的其中一个 WPF 按钮(红色框)。这将创建一个任务,该任务将查找一个弹出窗口。接下来,点击下面对应的 HTML 按钮(蓝色框)来创建弹出窗口。你会注意到,一旦按钮被任务中运行的代码“按下”,窗口就会消失。
HTML 确认按钮将弹出另一个窗口。你必须手动点击按钮才能使该框消失。我这样做是为了表明在按钮被按下后,脚本将继续运行。
历史
无。