Skip to main content
 首页 » 编程设计

c#之WebBrowser Navigate 和 InvokeScript 的流程

2024年02月13日27kerrycode

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

    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 

当我单步执行它时,它似乎并没有“及时”调用脚本的 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; 
}