15

長年の読者、初めてのポスターはこちら。

私の目標は、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
}

しかし、これを処理するためのより効率的で適切な方法があるかどうか疑問に思っています。具体的には、ブロッキングManualResetEventを使用したTask.Factory.CreateNewです。ご入力いただきありがとうございます。

4

4 に答える 4

12

まず、これは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番目の呼び出しがあるとどうなるかを考えてみてください。

于 2011-12-22T22:37:14.723 に答える
2

を使用TaskCompletionSource<T>してタスクを作成し、後で完了としてマークすることができます。

非ジェネリックタスクの代替案はありませんが、Task<T>から派生したものとしてTask、aを使用しTaskCompletionSource<object>て結果をnullに設定することができます。

于 2011-12-22T22:22:37.657 に答える
0

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,
}
于 2017-09-27T18:54:50.997 に答える
-1

私は今日、stackoverflowに関する別の投稿の助けを借りて、このクラスを作成しました。(Async / Await)を使用して、スレッドをブロックせずにWebブラウザーコントロールを準備したいと考えています。

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
于 2014-02-23T10:47:31.893 に答える