Async/Await implementation of WebBrowser class for .NET

前端 未结 4 1301
后悔当初
后悔当初 2020-12-05 08:34

Longtime reader, first-time poster here.

My goal: To be able to take advantage of async/await while using the WebBrowser class. As the WebBrowser.Navigate(string ur

相关标签:
4条回答
  • 2020-12-05 08:43

    I created this class today, with the help of another post on stackoverflow, I want to get the ready webbrowser control without any thread blocking using (Async/Await).

    Dim bb = New wbBrowser
    Dim wb = Await bb.GetBrowserAsync("http://www.msn.com")
    

    Here is the class:

    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
    
    0 讨论(0)
  • 2020-12-05 08:49

    First off, I think this is a great exercise for learning how async/await works.

    You seem to be jumping through hoops in order to make NavigateAsync return a Task. But it doesn't have to return a Task in order to be awaitable! A method that contains an await must return Task, but a method that is awaitable need not return Task; all it has to do is return some type that you can call GetAwaiter on.

    You might consider implementing a little type like this:

    public struct WebBrowserAwaiter<T>
    {
        public bool IsCompleted { get { ... } }
        public void OnCompleted(Action continuation) { ... }
        public T GetResult() { ... }
    }
    

    and have NavigateAsync return some type upon which you can call GetAwaiter that returns a WebBrowserAwaiter. No need to build up a Task just to get its GetAwaiter method when you can make your own.

    More generally, something you might want to give some thought to is what happens if there is a second call to NavigateAsync while the first one is still navigating?

    0 讨论(0)
  • 2020-12-05 08:58

    You can use TaskCompletionSource<T> to create a Task and mark it as completed later.

    I don't see any alternative for the non-generic task, but as Task<T> derives from Task, you could just use a TaskCompletionSource<object> and set the result to null.

    0 讨论(0)
  • 2020-12-05 09:05

    I translated Vaibhav's VB code to C#. it's an amazing solution, I don't know why you are disappoint on it.

    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,
    }
    
    0 讨论(0)
提交回复
热议问题