c# - WebBrowser Navigate 和 InvokeScript 的流程

标签 c# javascript forms browser webbrowser-control

我无法理解我正在构建的这个函数的流程。

    public void PortalLogin(AutoResetEvent signal)
            {
                // Navigate to portal
                string portalUrl = "website_name";
                string portalEmail = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="89ece4e8e0e5c9ece4e8e0e5a7eae6e4" rel="noreferrer noopener nofollow">[email protected]</a>";
                string portalPassword = "password";
                Action action2 = () =>
                {
                    webBrowser2.Tag = signal;
                    webBrowser2.Navigate(portalUrl);
                    webBrowser2.DocumentCompleted -= WebBrowserDocumentCompleted;
                    webBrowser2.DocumentCompleted += WebBrowserDocumentCompleted;
                };
                webBrowser2.Invoke(action2);
                signal.WaitOne();

                // Login to O365 portal
                webBrowser2.Invoke(new Action(() =>
                {
                    HtmlElement head = webBrowser2.Document.GetElementsByTagName("head")[0];
                    HtmlElement testScript = webBrowser2.Document.CreateElement("script");
                    IHTMLScriptElement element = (IHTMLScriptElement)testScript.DomElement;
                    element.text = "function PortalLogin() { document.getElementById('userid').value = '" + portalEmail + "'; document.getElementById('password').value = '" + portalPassword + "';  document.getElementById('login').submit(); }";
                    head.AppendChild(testScript);
                    webBrowser2.Document.InvokeScript("PortalLogin");
                }));
            }

... more functions after this

当我单步执行它时,它似乎没有“及时”调用脚本的 document.getElementById('login').submit(); 部分。如何确保在 InvokeScript 完全完成之前不会发生任何事情?

此外,如果您看到任何多余的代码或可以清理的内容,那也很棒。

编辑:这是 DocumentCompleted 函数。

private void WebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs Url)
        {
            ((AutoResetEvent)((WebBrowser)sender).Tag).Set();
        }

最佳答案

几点:

您可以添加DocumentCompleted事件处理程序一旦超出 PortalLogin并重用相同的处理程序。您正在使用AutoResetEvent signal.WaitOne() 之后自动重置为无信号状态,所以您应该只需要一个 DocumentCompleted 的永久处理程序即可。 .

你确定吗document.getElementById('login')返回有效元素 submit方法可用吗?调用 InvokeScript 之前请确认。您可以分两步进行登录,例如:

            element.text = "function PortalLogin() { document.getElementById('userid').value = '" + portalEmail + "'; document.getElementById('password').value = '" + portalPassword + "';  }" +
                "function ExecuteLogin() { document.getElementById('login').submit(); }";
            head.AppendChild(testScript);
            webBrowser2.Document.InvokeScript("PortalLogin");
            // verify document.getElementById('login') here
            webBrowser2.Document.InvokeScript("ExecuteLogin");

注意:如果成功,提交最终会触发另一个DocumentCompleted事件。

我将使用单线程和 await/async pattern 重构此代码。 DocumentCompleted可以用 TaskCompletionSource 包装为任务(这里是 how )。

下面是使用 async/await 的样子。在有 MessageBox.Show 的地方你可以进行 DOM 操作。请注意,这一切都是在主 UI 线程上完成的(当然是异步的)。对我来说看起来容易一些。

void Form1_Load(object sender, EventArgs e)
{
    var task = DoNavigationAsync();
    task.ContinueWith((t) =>
    {
        MessageBox.Show("Navigation done!");
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

struct Void {}; // use an empty struct as parameter to generic TaskCompletionSource

async Task DoNavigationAsync()
{
    Void v;
    TaskCompletionSource<Void> tcs = null; 
    WebBrowserDocumentCompletedEventHandler documentComplete = null;

    documentComplete = new WebBrowserDocumentCompletedEventHandler((s, e) =>
    {
        // more of DocumentCompleted can possibly be fired due to dynamic navigation inside the web page, we don't want them!
        this.WB.DocumentCompleted -= documentComplete;              
        tcs.SetResult(v); // continue from where awaited
    });

    // navigate to www.bing.com
    tcs = new TaskCompletionSource<Void>();
    this.WB.DocumentCompleted += documentComplete;
    this.WB.Navigate("http://www.bing.com");
    await tcs.Task;
    // do whatever you want with this instance of WB.Document
    MessageBox.Show(this.WB.Document.Url.ToString());

    // navigate to www.google.com
    tcs = new TaskCompletionSource<Void>();
    this.WB.DocumentCompleted += documentComplete;
    this.WB.Navigate("http://www.google.com");
    await tcs.Task;
    // do whatever you want with this instance of WB.Document
    MessageBox.Show(this.WB.Document.Url.ToString());

    // navigate to www.yahoo.com
    tcs = new TaskCompletionSource<Void>();
    this.WB.DocumentCompleted += documentComplete;
    this.WB.Navigate("http://www.yahoo.com");
    await tcs.Task;
    // do whatever you want with this instance of WB.Document
    MessageBox.Show(this.WB.Document.Url.ToString());

    return;
}

关于c# - WebBrowser Navigate 和 InvokeScript 的流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18280487/

相关文章:

css - 表格没有清除自己的默认值?

php - 在 Laravel 5.3 中使用相同的表单进行创建和编辑

php - PHP 文件中的 CSS 表单边框格式

c# - 如何根据名称比较两个文件夹中的不同文件?

c# - 我怎么知道什么时候调用 Dispose 是安全的?

Javascript String.replace 与动态正则表达式?

javascript - 使用 Javascript 搜索具有精确标签的 JSON

c# - 使用 DDD 限界上下文的实体配置管理

c# - 三角网格的良好数据结构

javascript - 等价于 Angular 或纯 JS 中的 $.param?