我无法理解我正在构建的这个功能的流程。
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();
}
答案 0 :(得分:4)
几点:
您可以在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;
}