2017-08-29 8 views
0

TaskCompletionSourceの周りを頭で囲みます。ここに私が同期的に書いた小さなクラスがあります(WebBrowser.Navigate()は非同期です)。ウェブページをダウンロードして呼び出し元に返します。 TaskCompletionSourceを正しく使用したかどうかはわかりません。誰かが私がここで紛失していることを示してもらえますか、それとも完全に工学的な解決策であるのかを教えてください。WebBrowserコントロール:同期ラッパーの作成

class PageDownloader 
{ 
    private WebBrowser _WB = new WebBrowser(); 
    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); 

    public PageDownloader() 
    { 
    _WB.LoadCompleted += _WB_LoadCompleted; 
    } 

    public string Download(string url) 
    { 
    _WB.Navigate(new Uri(url)); 

    tcs.Task.Wait(); 

    if (tcs.Task.IsCanceled || tcs.Task.IsFaulted) 
     return null; 
    else 
     return (_WB.Document as mshtml.HTMLDocument).body.innerHTML; 
    } 

    private void _WB_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) 
    { 
    var docTemp = _WB.Document as mshtml.HTMLDocument; 
    foreach (mshtml.IHTMLImgElement imgElemt in docTemp.images) 
     imgElemt.src = ""; 

    tcs.SetResult(true); 
    } 
} 
+0

ダウンロード方法が非同期ではないので、実際にそれを待つことができますか? – mm8

+0

@ mm8:いいえ、それはクラスの全体のポイントです。私はそれの "同期"バージョンを作成しようとしている。 – dotNET

+0

次に、WebBrowserコントロールのLoadCompletedイベントを直接処理することはできますか? – mm8

答えて

1

私はあなたがここにasyncによって何を意味するかわからないけどWebBrowser.Navigate方法は、単純にvoidを返し、C#5で導入されたasync/awaitキーワードを使用して待たすることはできません。ナビゲーション操作が開始され、すぐに戻ります。ナビゲーションが実際に完了したら何かをしたい場合は、LoadCompletedイベントハンドラに登録する必要があります。ここまでは順調ですね。 TaskCompletionSource<T>を使用して

あなたが実際にあなたのクラスasyncそうすることができます実際にawait結果にDownload方法を作ることができます。

class PageDownloader 
{ 
    private WebBrowser _WB = new WebBrowser(); 
    private TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(); 

    public PageDownloader() 
    { 
     _WB.LoadCompleted += _WB_LoadCompleted; 
    } 

    public async Task<string> DownloadAsync(string url) 
    { 
     _WB.Navigate(new Uri(url)); 

     await tcs.Task.ConfigureAwait(false); 

     if (tcs.Task.IsCanceled || tcs.Task.IsFaulted) 
      return null; 
     else 
      return tcs.Task.Result; 
    } 

    private void _WB_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) 
    { 
     try 
     { 
      var docTemp = _WB.Document as mshtml.HTMLDocument; 
      foreach (mshtml.IHTMLImgElement imgElemt in docTemp.images) 
       imgElemt.src = ""; 

      tcs.SetResult(docTemp.body.innerHTML); 
     } 
     catch(Exception ex) 
     { 
      tcs.SetException(ex); 
     } 
    } 
} 

使用法:

PageDownloader downloader = new PageDownloader(); 
string html = await downloader.DownloadAsync("http://stackoverflow.com"); 
//or if you want to block synchronously 
string html = downloader.DownloadAsync("http://stackoverflow.com").Result; 

あなたはおそらくも、あなたのクラスでの同期のオーバーロードを作成することができます。

あなたはおそらく、あなたの LoadCompletedイベントハンドラ内で発生する可能性のある例外をキャッチしたいです
public string Download(string url) 
{ 
    return DownloadAsync(url).Result; 
} 
+0

私はクリックするのが速すぎたかもしれません。あなたのコードは 'await'行にぶつかるが、決してそこから前進することはない。あなたはそれを素早く試すことができますか? – dotNET

+0

どちらが待っていますか? SetResultメソッドが呼び出されますか? – mm8

+0

LoadCompletedイベントが発生するには、WebBrowserをビジュアルツリーに追加する必要があります。したがって、表示されない新しいクラスを作成するのではなく、XAMLで定義したWebBroswerコントロールをクラスに挿入することができます。しかし、これはTaskCompletionSourceとは関係ありません。 – mm8

0

私のために今まで働いてきた唯一の解決策は、Windows Forms WebBrowserコントロールを次のコードで使用することです。

public string Download(string url) 
{ 
    bool flag = false; 

    using (System.Windows.Forms.WebBrowser WB = new System.Windows.Forms.WebBrowser()) 
    { 
    WB.DocumentCompleted += (sender, e) => 
    { 
     var docTemp = WB.Document; 
     foreach (HtmlElement imgElemt in docTemp.Images) 
     imgElemt.SetAttribute("src", ""); 

     flag = true; 
    }; 

    WB.Navigate(url); 

    while (!flag) 
     Application.DoEvents(); 

    return WB.Document.Body.InnerHtml; 
    } 
} 

私は単純に、このコードではWPF WebBrowser制御とWinFormsのバージョンを置き換えた場合でも、このコードは、これまでreturnラインをヒットしません。

良い答えを探しています。

関連する問題