web浏览器导航和InvokeScript流 [英] Flow of WebBrowser Navigate and InvokeScript

查看:135
本文介绍了web浏览器导航和InvokeScript流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解这个功能我建立的流程。

 公共无效PortalLogin(的AutoResetEvent信号)
            {
                //导航到门户
                字符串portalUrl =WEBSITE_NAME;
                字符串portalEmail =email@email.com;
                字符串portalPassword =密码;
                操作动作2 =()=>
                {
                    webBrowser2.Tag =信号;
                    webBrowser2.Navigate(portalUrl);
                    webBrowser2.DocumentCompleted - = WebBrowserDocumentCompleted;
                    webBrowser2.DocumentCompleted + = WebBrowserDocumentCompleted;
                };
                webBrowser2.Invoke(动作2);
                signal.WaitOne();                //登录门户O365
                webBrowser2.Invoke(新动作(()=>
                {
                    的HtmlElement头= webBrowser2.Document.GetElementsByTagName(头)[0];
                    的HtmlElement testScript = webBrowser2.Document.CreateElement(脚本);
                    IHTMLScriptElement元=(IHTMLScriptElement)testScript.DomElement;
                    element.text =功能PortalLogin(){的document.getElementById('用户id')值='+ portalEmail +;的document.getElementById('密码')值='+ portalPassword +;文件。的getElementById('登录')提交();};
                    head.AppendChild(testScript);
                    webBrowser2.Document.InvokeScript(PortalLogin);
                }));
            }......在这之后更多的功能

当我通过它一步,它似乎并没有被调用的document.getElementById('登录')提交();在脚本的部分时间。我怎样才能确保没有任何反应,直到 InvokeScript 完全完成?

须─如果你看到任何多余的code或东西,可以被清理,这是真棒太。

修改:下面是DocumentCompleted功能

 私人无效WebBrowserDocumentCompleted(对象发件人,WebBrowserDocumentCompletedEventArgs URL)
        {
            ((的AutoResetEvent)((web浏览器)发送方).TAG)。设置();
        }


解决方案

下面的几点:

<击> - 这似乎是错的:

  webBrowser2.DocumentCompleted  -  = WebBrowserDocumentCompleted;
            webBrowser2.DocumentCompleted + = WebBrowserDocumentCompleted;

当你用这样的语法事件处理程序,C#编译器暗中为您创建一个独特的事件处理程序对象,如新WebBrowserDocumentCompletedEventHandler(WebBrowserDocumentCompleted)。所以,你用删除哪些 - = 不是你有什么$ P $与 + = pviously添加,您也可以结束了旧的处理程序被一再呼吁。

我上面说的不正确,你确实的可以的删除处理的这样

不过,你可以一次外 PortalLogin 添加和重用相同的处理。您正在使用的AutoResetEvent 这之后自动复位到无信号状态signal.WaitOne(),所以你应该罚款只用一个永久性处理程序 DocumentCompleted


  • 您确定的document.getElementById('登录')返回的有效元素提交方法可用?验证调用 InvokeScript 之前。你可以做两个步骤的登录,例如:

      element.text =功能PortalLogin(){的document.getElementById('用户id')值='+ portalEmail +;的document.getElementById('密码')。值='+ portalPassword +';}+
                功能ExecuteLogin(){的document.getElementById('密码')提交();};
            head.AppendChild(testScript);
            webBrowser2.Document.InvokeScript(PortalLogin);
            //验证的document.getElementById('登录')在这里
            webBrowser2.Document.InvokeScript(ExecuteLogin);


  • 请注意:如果成功,提交最终会引发另一 DocumentCompleted 事件


我会使用单线程和的await /重构这个code异步模式的。 DocumentCompleted 可以包装成与 TaskCompletionSource (这里的如何)。

[更新]

下面是怎么看起来像使用异步/的await 。在地方有 MessageBox.Show ,你可以做你的DOM操作。请注意,这一切都完成的主要UI线程(异步,当然)。看起来相当容易一点给我。

 无效Form1_Load的(对象发件人,EventArgs的发送)
{
    变种任务= DoNavigationAsync();
    task.ContinueWith((T)=&GT;
    {
        MessageBox.Show(航海完成了!);
    },TaskScheduler.FromCurrentSynchronizationContext());
}结构虚空{}; //使用空结构作为参数,以通用TaskCompletionSource异步任务DoNavigationAsync()
{
    空隙部V;
    TaskCompletionSource&LT;无效&GT; TCS = NULL;
    WebBrowserDocumentCompletedEventHandler的DocumentComplete = NULL;    的DocumentComplete =新WebBrowserDocumentCompletedEventHandler((S,E)=&GT;
    {
        //更多DocumentCompleted能可能被由于网页里被炒到动态导航,我们不希望他们!
        this.WB.DocumentCompleted - =的DocumentComplete;
        tcs.SetResult(五); //从那里继续等待
    });    //浏览www.bing.com
    TCS =新TaskCompletionSource&LT;无效&GT;();
    this.WB.DocumentCompleted + =的DocumentComplete;
    this.WB.Navigate(http://www.bing.com);
    等待tcs.Task;
    //做任何你想WB.Document的这个实例
    MessageBox.Show(this.WB.Document.Url.ToString());    //浏览到www.google.com
    TCS =新TaskCompletionSource&LT;无效&GT;();
    this.WB.DocumentCompleted + =的DocumentComplete;
    this.WB.Navigate(http://www.google.com);
    等待tcs.Task;
    //做任何你想WB.Document的这个实例
    MessageBox.Show(this.WB.Document.Url.ToString());    //浏览到www.yahoo.com
    TCS =新TaskCompletionSource&LT;无效&GT;();
    this.WB.DocumentCompleted + =的DocumentComplete;
    this.WB.Navigate(http://www.yahoo.com);
    等待tcs.Task;
    //做任何你想WB.Document的这个实例
    MessageBox.Show(this.WB.Document.Url.ToString());    返回;
}

I'm having trouble understanding the flow of this function I'm building.

    public void PortalLogin(AutoResetEvent signal)
            {
                // Navigate to portal
                string portalUrl = "website_name";
                string portalEmail = "email@email.com";
                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

When I step through it, it doesn't seem to be invoking the document.getElementById('login').submit(); part of the script "in time". How can I make sure nothing happens until the InvokeScript has fully completed?

Also- if you see any superfluous code or stuff that can be cleaned up, that's awesome too.

EDIT: Here is DocumentCompleted function.

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

解决方案

A few points here:

- This appears to be wrong:

            webBrowser2.DocumentCompleted -= WebBrowserDocumentCompleted;
            webBrowser2.DocumentCompleted += WebBrowserDocumentCompleted;

When you use such syntax for event handlers, C# compiler implicitly creates an unique event handler object for you, like new WebBrowserDocumentCompletedEventHandler(WebBrowserDocumentCompleted). So what you remove with -= is not what you have previously added with +=, and you may end up with old handlers being called again and again.

[EDITED] What I said above is incorrect, you indeed can remove the handlers that way.

Still, you can add it once outside PortalLogin and reuse the same handler. You are using AutoResetEvent which automatically resets into non-signaled state after signal.WaitOne(), so you should be fine with just one permanent handler for DocumentCompleted.

  • Are you sure document.getElementById('login') returns a valid element with submit method available? Verify that before calling InvokeScript. You could do the login in two steps, e.g.:

            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");
    

  • Note: if successful, the submit would eventually trigger another DocumentCompleted event.

[EDITED]

I would refactor this code using single thread and await/async pattern. DocumentCompleted can be wrapped as a task with TaskCompletionSource (here's how).

[UPDATE]

Below is how it might look like using async/await. In places where there's MessageBox.Show you could do your DOM manipulations. Note, it's all done on the main UI thread (asynchronously, of course). Looks quite a bit easier to me.

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;
}

这篇关于web浏览器导航和InvokeScript流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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