C# WebBrowser 控件 - 使用 InvokeMember ("Click"提交表单不工作)

标签 c# forms automation submit webbrowser-control

我正在编写自动化测试脚本并使用 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");
                }
            }

任何帮助将不胜感激。谢谢。

最佳答案

以下代码适用于我,使用问题评论中的实时表单操作 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://stackoverflow.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>

关于C# WebBrowser 控件 - 使用 InvokeMember ("Click"提交表单不工作),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19044659/

相关文章:

c# - 如何在不依赖公共(public)数据上下文实例的情况下保持相关实体之间的引用透明性?

php - 如何让 Contact form 7 返回复选框标签而不是post表单时的值数据?

java - 显式等待给定操作(错误) - Selenium Webdriver

Python - 创建 self 复制的类对象实例

c# - AutomationElement 使用 Inspect.exe 显示,但在使用 UIAutomationCore.dll 或 System.Windows.Automation 时不显示

c# - 异步等待在 C# 中丢失响应

c# - Entity Framework 6 中的所有查询是否都已编译?

c# - 如何正确处置 IHttpModule?

html - 多列网格中的 Bootstrap 垂直制表符

html - 如何以负责任的形式 css 替换固定值