C# WebBrowser 控件 - 使用 InvokeMember("Click") 提交表单不起作用 [英] C# WebBrowser Control - Form Submit Not Working using InvokeMember("Click")

查看:30
本文介绍了C# WebBrowser 控件 - 使用 InvokeMember("Click") 提交表单不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写自动化测试脚本并使用 WebBrowser 控件.我正在尝试提交以下 HTML 并在用户接受服务条款时进行测试:

I am working on automated testing script and am using the WebBrowser control. I am trying to submit the following HTML and testing when the user accepts the terms of service:

    <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# 中的代码,但不提交表单.

Here is the code in C#, but does not submit the form.

            HtmlElementCollection el = webBrowser.Document.GetElementsByTagName("button");
            foreach (HtmlElement btn in el)
            {
                if (btn.InnerText == "ACCEPT the terms of use")
                {
                    btn.InvokeMember("Click");
                }
            }

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

Any help would be much appreciated. Thanks.

推荐答案

以下代码对我有用,使用问题评论中的实时表单操作 URL,并在 IE10 上进行了测试.按原样尝试.如果它也适用于您,请随意将其用作 Web 自动化任务的模板.几点:

The following code works for me, using the live form action URL from the question comments, tested with IE10. Try it as is. If it works for you as well, feel free to use it as a template for your web automation tasks. A couple of points:

  • FEATURE_BROWSER_EMULATION 用于确保 WebBrowser 的行为方式与独立 IE 浏览器相同(或尽可能接近).这对于几乎所有基于 WebBrowser 的项目来说都是必须的.我相信这应该有助于解决您身边的原始问题.

  • FEATURE_BROWSER_EMULATION is used to make sure the WebBrowser behaves in the same way as standalone IE browser (or as close as possible). This is a must for almost any WebBrowser-based project. I believe that's what should help to solve the original problem on your side.

异步代码用于提高自动化逻辑的可靠性,添加支持超时和取消并促进自然线性代码流(使用async/await).

Asynchronous code is used to improve the automation logic reliability, add support timeouts and cancellation and promote natural linear code flow (using 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(@"SoftwareMicrosoftInternet ExplorerMainFeatureControl", feature),
                RegistryKeyPermissionCheck.ReadWriteSubTree))
            {
                key.SetValue(appName, (UInt32)value, RegistryValueKind.DWord);
            }
        }

        UInt32 GetBrowserEmulationMode()
        {
            int browserVersion = 7;
            using (var ieKey = Registry.LocalMachine.OpenSubKey(@"SOFTWAREMicrosoftInternet 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的内容:

The content of 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") 提交表单不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆