C# WebBrowser 控件 - 使用 InvokeMember("Click") 提交表单不起作用
Posted
技术标签:
【中文标题】C# WebBrowser 控件 - 使用 InvokeMember("Click") 提交表单不起作用【英文标题】:C# WebBrowser Control - Form Submit Not Working using InvokeMember("Click") 【发布时间】:2013-10-03 09:22:49 【问题描述】:我正在编写自动化测试脚本并使用 WebBrowser 控件。当用户接受服务条款时,我正在尝试提交以下 html 并进行测试:
<form action="http://post.dev.dealerconnextion/k/6hRbDTwn4xGVl2MHITQsBw/hrshq" method="post">
<input name="StepCheck" value="U2FsdGVkX18zMTk5MzE5OUgFyFgD3V5yf5Rwbtfhf3gjdH4KSx4hqj4vkrw7K6e-" type="hidden">
<button type="submit" name="continue" value="y">ACCEPT the terms of use</button>
<button type="submit" name="continue" value="n">DECLINE the terms of use</button>
</form>
// Terms of Use Information
<form action="http://post.dev.dealerconnextion/k/6hRbDTwn4xGVl2MHITQsBw/hrshq" method="post">
<input name="StepCheck" value="U2FsdGVkX18zMTk5MzE5OUgFyFgD3V5yf5Rwbtfhf3gjdH4KSx4hqj4vkrw7K6e-" type="hidden">
<button type="submit" name="continue" value="y">ACCEPT the terms of use</button>
<button type="submit" name="continue" value="n">DECLINE the terms of use</button>
</form>
这里是 C# 中的代码,但没有提交表单。
HtmlElementCollection el = webBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement btn in el)
if (btn.InnerText == "ACCEPT the terms of use")
btn.InvokeMember("Click");
任何帮助将不胜感激。谢谢。
【问题讨论】:
您应该在发生DocumentCompleted
事件时致电btn.InvokeMember("Click")
。如果这就是你所做的,并且你在该行上放置了一个断点,它会在调试器中被命中吗?
我确实添加了一个 DocumentCompleted 事件。是的, btn.InvokeMember("Click") 确实在调试器中被击中。只是什么都没发生。
当从DocumentComplete
调用时,您的代码适用于我(使用自定义本地操作 URL)。
你能告诉我你正在使用的代码 - 只是为了清楚 - 谢谢
代码很简单:pastebin.com/M08bxjwP。 test.html
包含从您的问题复制的 HTML(除了 URL)。
【参考方案1】:
以下代码适用于我,使用问题 cmets 中的实时表单操作 URL,并使用 IE10 进行了测试。按原样尝试。如果它也适用于您,请随意将其用作您的 Web 自动化任务的模板。几点:
FEATURE_BROWSER_EMULATION 用于确保WebBrowser
的行为方式与独立 IE 浏览器相同(或尽可能接近)。 这对于几乎所有基于WebBrowser
的项目来说都是必须的。我相信这应该有助于解决您的原始问题。
异步代码用于提高自动化逻辑可靠性,添加支持超时和取消,促进自然的线性代码流(使用async/await
)。
C#:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WebAutomation
// http://***.com/q/19044659/1768303
public partial class MainForm : Form
WebBrowser webBrowser;
// non-deterministic delay to let AJAX code run
const int AJAX_DELAY = 1000;
// keep track of the main automation task
CancellationTokenSource mainCts;
Task mainTask = null;
public MainForm()
SetBrowserFeatureControl(); // set FEATURE_BROWSER_EMULATION first
InitializeComponent();
InitBrowser();
this.Load += (s, e) =>
// start the automation when form is loaded
// timeout the whole automation task in 30s
mainCts = new CancellationTokenSource(30000);
mainTask = DoAutomationAsync(mainCts.Token).ContinueWith((completedTask) =>
Trace.WriteLine(String.Format("Automation task status: 0", completedTask.Status.ToString()));
, TaskScheduler.FromCurrentSynchronizationContext());
;
this.FormClosing += (s, e) =>
// cancel the automation if form closes
if (this.mainTask != null && !this.mainTask.IsCompleted)
mainCts.Cancel();
;
// create a WebBrowser instance (could use an existing one)
void InitBrowser()
this.webBrowser = new WebBrowser();
this.webBrowser.Dock = DockStyle.Fill;
this.Controls.Add(this.webBrowser);
this.webBrowser.Visible = true;
// the main automation logic
async Task DoAutomationAsync(CancellationToken ct)
await NavigateAsync(ct, () => this.webBrowser.Navigate("http://localhost:81/test.html"), 10000); // timeout in 10s
// page loaded, log the page's HTML
Trace.WriteLine(GetBrowserDocumentHtml());
// do the DOM automation
HtmlElementCollection all = webBrowser.Document.GetElementsByTagName("button");
// throw if none or more than one element found
HtmlElement btn = all.Cast<HtmlElement>().Single(
el => el.InnerHtml == "ACCEPT the terms of use");
ct.ThrowIfCancellationRequested();
// simulate a click which causes navigation
await NavigateAsync(ct, () => btn.InvokeMember("click"), 10000); // timeout in 10s
// form submitted and new page loaded, log the page's HTML
Trace.WriteLine(GetBrowserDocumentHtml());
// could continue with another NavigateAsync
// othrwise, the automation session completed
// Get the full HTML content of the document
string GetBrowserDocumentHtml()
return this.webBrowser.Document.GetElementsByTagName("html")[0].OuterHtml;
// Async navigation
async Task NavigateAsync(CancellationToken ct, Action startNavigation, int timeout = Timeout.Infinite)
var onloadTcs = new TaskCompletionSource<bool>();
EventHandler onloadEventHandler = null;
WebBrowserDocumentCompletedEventHandler documentCompletedHandler = delegate
// DocumentCompleted may be called several time for the same page,
// beacuse of frames
if (onloadEventHandler != null || onloadTcs == null || onloadTcs.Task.IsCompleted)
return;
// handle DOM onload event to make sure the document is fully loaded
onloadEventHandler = (s, e) =>
onloadTcs.TrySetResult(true);
this.webBrowser.Document.Window.AttachEventHandler("onload", onloadEventHandler);
;
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
if (timeout != Timeout.Infinite)
cts.CancelAfter(Timeout.Infinite);
using (cts.Token.Register(() => onloadTcs.TrySetCanceled(), useSynchronizationContext: true))
this.webBrowser.DocumentCompleted += documentCompletedHandler;
try
startNavigation();
// wait for DOM onload, throw if cancelled
await onloadTcs.Task;
ct.ThrowIfCancellationRequested();
// let AJAX code run, throw if cancelled
await Task.Delay(AJAX_DELAY, ct);
finally
this.webBrowser.DocumentCompleted -= documentCompletedHandler;
if (onloadEventHandler != null)
this.webBrowser.Document.Window.DetachEventHandler("onload", onloadEventHandler);
// Browser feature conntrol
void SetBrowserFeatureControl()
// http://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx
// FeatureControl settings are per-process
var fileName = System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
// make the control is not running inside Visual Studio Designer
if (String.Compare(fileName, "devenv.exe", true) == 0 || String.Compare(fileName, "XDesProc.exe", true) == 0)
return;
SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, GetBrowserEmulationMode()); // Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode.
void SetBrowserFeatureControlKey(string feature, string appName, uint value)
using (var key = Registry.CurrentUser.CreateSubKey(
String.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),
RegistryKeyPermissionCheck.ReadWriteSubTree))
key.SetValue(appName, (UInt32)value, RegistryValueKind.DWord);
UInt32 GetBrowserEmulationMode()
int browserVersion = 7;
using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Internet Explorer",
RegistryKeyPermissionCheck.ReadSubTree,
System.Security.AccessControl.RegistryRights.QueryValues))
var version = ieKey.GetValue("svcVersion");
if (null == version)
version = ieKey.GetValue("Version");
if (null == version)
throw new ApplicationException("Microsoft Internet Explorer is required!");
int.TryParse(version.ToString().Split('.')[0], out browserVersion);
UInt32 mode = 10000; // Internet Explorer 10. Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode. Default value for Internet Explorer 10.
switch (browserVersion)
case 7:
mode = 7000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. Default value for applications hosting the WebBrowser Control.
break;
case 8:
mode = 8000; // Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. Default value for Internet Explorer 8
break;
case 9:
mode = 9000; // Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode. Default value for Internet Explorer 9.
break;
default:
// use IE10 mode by default
break;
return mode;
http://localhost:81/test.html
的内容:
<!DOCTYPE html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
</head>
<body>
<form action="<the URL from OP's comments>" method="post">
<input name="StepCheck" value="U2FsdGVkX18zMTk5MzE5OUgFyFgD3V5yf5Rwbtfhf3gjdH4KSx4hqj4vkrw7K6e-" type="hidden">
<button type="submit" name="continue" value="y">ACCEPT the terms of use</button>
<button type="submit" name="continue" value="n">DECLINE the terms of use</button>
</form>
</body>
【讨论】:
请问this.FormClosing += (s, e) =>
的代码是做什么的?谢谢。
@Sabuncu,它处理FormClosing
事件以取消任何挂起的后台导航操作。
@Noseratio 解决方案是如此晦涩难懂,以至于我需要很长时间才能找到。我的支持和感谢 =)
请问HTML表单标签是否包含相对路径代码不起作用?有什么问题吗?你能提供这个问题的答案吗:***.com/questions/69567116/…
@Hardik 无法回答这个问题,但你得到的答案/cmets 很好。你真的应该在 2021 年使用 WebView2 和 IE11 WebBrowser
。【参考方案2】:
这对我有用,如下所示。可能对某人有用。
首先,我在获得焦点时为按钮元素创建一个事件处理程序。一旦所有其他表单元素都填充了适当的值,您应该将焦点放在按钮上,如下所示:
HtmlElement xUsername = xDoc.GetElementById("username_txt");
HtmlElement xPassword = xDoc.GetElementById("password_txt");
HtmlElement btnSubmit = xDoc.GetElementById("btnSubmit");
if (xUsername != null && xPassword != null && btnSubmit != null)
xUsername.SetAttribute("value", "testUserName");
xPassword.SetAttribute("value", "123456789");
btnSubmit.GotFocus += BtnSubmit_GotFocus;
btnSubmit.Focus();
那么事件处理程序的实现会是这样的:
private void BtnSubmit_GotFocus(object sender, HtmlElementEventArgs e)
var btnSubmit = sender as HtmlElement;
btnSubmit.RaiseEvent("onclick");
btnSubmit.InvokeMember("click");
【讨论】:
【参考方案3】:在我的情况下,我也无法通过简单地调用找到的元素的 Click 方法来点击元素。 有效的是 Ali Tabandeh 在上面的答案中列出的类似解决方案:
-
找到需要的html元素
定义适当的 GotFocus 事件处理程序
然后调用找到的元素的 Focus 方法。
GotFocus 的事件处理程序应该
-
RaiseEvent“点击”
调用“点击”方法
问题是这在我的情况下从第三次开始(调用 htmlelement.Focus() 需要 3 次)。
【讨论】:
以上是关于C# WebBrowser 控件 - 使用 InvokeMember("Click") 提交表单不起作用的主要内容,如果未能解决你的问题,请参考以下文章
在 C# webBrowser 控件中调用 Javascript 函数