.NET 的 WebBrowser 类的异步/等待实现
Posted
技术标签:
【中文标题】.NET 的 WebBrowser 类的异步/等待实现【英文标题】:Async/Await implementation of WebBrowser class for .NET 【发布时间】:2012-01-26 11:13:33 【问题描述】:老读者,第一次发帖。
我的目标:在使用 WebBrowser 类时能够利用 async/await。由于 WebBrowser.Navigate(string url) 是一个异步方法,在 LoadComplete 事件触发之前,您无法检查 html 文档。
到目前为止,这是我的(工作)代码:
public class AsyncWebBrowser
protected WebBrowser m_WebBrowser;
private ManualResetEvent m_MRE = new ManualResetEvent(false);
public void SetBrowser(WebBrowser browser)
this.m_WebBrowser = browser;
browser.LoadCompleted += new LoadCompletedEventHandler(WebBrowser_LoadCompleted);
public Task NavigateAsync(string url)
Navigate(url);
return Task.Factory.StartNew((Action)(() =>
m_MRE.WaitOne();
m_MRE.Reset();
));
public void Navigate(string url)
m_WebBrowser.Navigate(new Uri(url));
void WebBrowser_LoadCompleted(object sender, NavigationEventArgs e)
m_MRE.Set();
而上一堂课现在允许我使用以下内容:
public async void NavigateToGoogle()
await browser.NavigateAsync("www.google.com");
//Do any necessary actions on google.com
但是,我想知道是否有更有效/更合适的方法来处理这个问题。具体来说,Task.Factory.CreateNew 带有阻塞的 ManualResetEvent。感谢您的意见!
【问题讨论】:
【参考方案1】:首先,我认为这是学习 async/await 工作原理的一个很好的练习。
您似乎是为了让 NavigateAsync 返回一个任务而越过障碍。但它不必为了等待而返回任务!一个包含await的方法必须返回Task,但是一个可等待的方法不需要返回Task;它所要做的就是返回一些可以调用 GetAwaiter 的类型。
你可以考虑实现一个像这样的小类型:
public struct WebBrowserAwaiter<T>
public bool IsCompleted get ...
public void OnCompleted(Action continuation) ...
public T GetResult() ...
并让 NavigateAsync 返回某种类型,您可以在该类型上调用返回 WebBrowserAwaiter 的 GetAwaiter。当您可以自己创建任务时,无需构建一个任务来获取它的 GetAwaiter 方法。
更一般地说,您可能需要考虑一下如果第二次调用 NavigateAsync 而第一次调用仍在导航时会发生什么?
【讨论】:
你说得非常对,我花了相当长的时间让它正确返回一个任务,然后我可以等待。哈哈。我会尝试这个建议,看看我能不能想出一些东西。谢谢!【参考方案2】:您可以使用TaskCompletionSource<T>
来创建一个任务并将其标记为稍后完成。
我没有看到非通用任务的任何替代方案,但由于 Task<T>
派生自 Task
,您可以只使用 TaskCompletionSource<object>
并将结果设置为 null。
【讨论】:
感谢您的建议。我会看看这是否可以用来改进代码。 :)【参考方案3】:我将 Vaibhav 的 VB 代码翻译成 C#。这是一个了不起的解决方案,我不知道你为什么对它感到失望。
public class YourClassThatIsUsingWebBrowser : IDisposable
private WebBrowser browser;
private TaskCompletionSource<BrowserResult> tcs;
public YourClassThatIsUsingWebBrowser()
this.browser.DocumentCompleted += AsyncBrowser_DocumentCompleted;
this.browser.Document.Window.Error += (errorSender, errorEvent) =>
SetResult(BrowserResult.Exception, errorEvent.Description);
;
this.browser.PreviewKeyDown += Browser_PreviewKeyDown;
this.browser.Navigating += Browser_Navigating;
private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
tcs = new TaskCompletionSource<BrowserResult>();
private void Browser_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
if (e.KeyCode == Keys.Escape)
this.browser.Stop();
SetResult(BrowserResult.Cancelled);
private void AsyncBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
SetResult();
public async Task<BrowserResult> NavigateAsync(string urlString)
this.browser.Navigate(urlString);
return await tcs.Task;
private void SetResult(BrowserResult result = BrowserResult.Succeed, string error = null)
if (tcs == null)
return;
switch (result)
case BrowserResult.Cancelled:
tcs.SetCanceled();
break;
case BrowserResult.Exception:
tcs.SetException(new Exception(error));
break;
case BrowserResult.Succeed:
default:
tcs.SetResult(result);
break;
public void Dispose()
Dispose(true);
GC.SuppressFinalize(this);
bool disposed = false;
protected void Dispose(bool disposing)
if (!disposed)
if (disposing)
this.browser.Dispose();
disposed = true;
public enum BrowserResult
Succeed,
Cancelled,
Exception,
【讨论】:
【参考方案4】:我今天创建了这个类,在 *** 上的另一篇文章的帮助下,我想使用 (Async/Await) 获得准备好的 webbrowser 控件,而无需任何线程阻塞。
Dim bb = New wbBrowser
Dim wb = Await bb.GetBrowserAsync("http://www.msn.com")
这是课程:
Imports System.Threading
Imports System.Threading.Tasks
Public Class wbBrowser
Implements IDisposable
Dim m_wbBrowser As New WebBrowser
Dim m_tcs As TaskCompletionSource(Of WebBrowser)
Public Sub New()
m_wbBrowser.ScrollBarsEnabled = False
m_wbBrowser.ScriptErrorsSuppressed = False
AddHandler m_wbBrowser.DocumentCompleted, Sub(s, args) m_tcs.SetResult(m_wbBrowser)
End Sub
Public Async Function GetBrowserAsync(ByVal URL As String) As Task(Of WebBrowser)
m_wbBrowser.Navigate(URL)
Return Await WhenDocumentCompleted(m_wbBrowser)
End Function
Private Function WhenDocumentCompleted(browser As WebBrowser) As Task(Of WebBrowser)
m_tcs = New TaskCompletionSource(Of WebBrowser)
Return m_tcs.Task
End Function
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
m_wbBrowser.Dispose()
End If
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
【讨论】:
以上是关于.NET 的 WebBrowser 类的异步/等待实现的主要内容,如果未能解决你的问题,请参考以下文章
用Webbrowser怎么实现获取网页内容并自动点击?(VB.NET)