webView2コントロールの画面キャプチャをしたくて


WEB画面を取り込んでいろいろやろうと思っている
個人開発しているソフトがあるのだけど…

最近のサイトはie未対応でvb.netに付属しているWebBrowserコントロールで読み込んでも、まともに表示されないものが増えてきた。

そこで「WebView2コントロールというのを使用するとそういうサイトも表示はできるらしい」と知ったので試してみた。

WebView2コントロールを使えば今までエラーになっていたサイトの表示はできる。
うん、”表示はだけは”できるんだ。
でも、こいつがvb.netの普通の手段では画面キャプチャできない。

こまった。
なので調べて、出来るようにしてみた。

webのロード部分などの説明は省くが最初に試したのはDrawToBitmapメソッドだ。
他のコントロールにも実装されている標準的なコントロールの表示イメージを画像にするだけのメソッド。
こいつを  WebView2.DrawToBitmap() のように呼び出してみる
そして得られたイメージを保存してみる。
うん、真っ白けだ。
画面イメージが取れてない。

なもんで、他になんかないのか？
と探してみたところ「CapturePreviewAsync」というメソッドがある。
このように使うらしい。

    Dim imageType As Integer = 1 ' 1:jpeg / 2:png
    Dim ts As Task = WebView2.CoreWebView2.CapturePreviewAsync(imageType, ms)

試しに、このようにしてみた。

    Dim imageType As Integer = 1 ' 1:jpeg / 2:png
    Dim ts As Task = WebView2.CoreWebView2.CapturePreviewAsync(imageType, ms)
    Dim img As Image = Image.FromStream(ms)
    Me.PictureBox1.Image = img

するとうまくいかない。
どうやらCapturePreviewAsyncメソッドが非同期で動くため
イメージがメモリーストリームに書き込まれる前にFromStreamで変換しているからだめらしい
多分WebView2コントロールでイベント処理をしないと画像が生成されないんだろう。

なもんで、こんな風にしてみた。

    Public Function webCapture(WebView2 As WinForms.WebView2) As Image
    'イメージ取得用のメモリーストリーム
    Dim ms As New MemoryStream()

    'WebView2の非同期画面キャプチャを実行
    Dim imageType As Integer = 1 ' 1:jpeg / 2:png
    Dim ts As Task = WebView2.CoreWebView2.CapturePreviewAsync(imageType, ms)

    '試行回数
    Dim retry As Integer = 100

    '待機時間
    Dim timeout As Integer = 100

    ' (待機時間)100ms * (試行回数)100回で最大１０秒待つ
    For i = 1 To retry
        ' スリープ処理
        System.Threading.Thread.Sleep(timeout)

        ' イベントを処理させる
        System.Windows.Forms.Application.DoEvents()

        ' CapturePreviewAsyncの非同期タスクが終了したか確認する
        If ts.Status = TaskStatus.RanToCompletion Then
            ' CapturePreviewAsyncの結果ストリームからイメージを習得
            Return Image.FromStream(ms)
        End If
    Next

    ' タイムアウト：イメージが取得できない
    Return Nothing
    End Function

上記のwebCapture関数をこう呼び出してimageを取得します。

    'WebView2ブラウザコントロールで表示しているイメージ取得
    Dim img As Image = webCapture(WebView21)

    '取得したイメージ表示
    Me.PictureBox1.Image = img

webCapture関数のポイントとしては 
試行回数まで回しtaskスレッドのStatusが完了になるまでDoEventsをしながらループさせることにある。
おそらくWebView2コントロールにCapturePreviewAsyncを投げてもWebView2側で処理するイベントが終わらない限りイメージをメモリーストリームに生成できないのだろう。
だからWebView2コントロール側でイメージメモリーストリームを生成できるようにDoEventsを仕込んだループでイベントを処理する余地を与えてあげるのね。
そんで適当な時間同期できるようにSleepもしてやる。
こちらで確認した限りでは試行回数のループは１～２回程度で処理できているようだ。
同期処理的に画像ファイルを取得できるのが良い。
もし、非同期的に処理したいならtaskスレッドのStatusが完了になるまでタイマーとかで監視してもいいし、別スレッドで非同期に処理させてもいいんじゃないかな。
僕が作っているソフトの関係では同期的に画像がイメージが取得できる、この関数が非常に都合がいいのだけど。

多少、強引な気はするけど一応の目的は達成できたのでヨシとする。
本当は非同期で実行とかやるべきなんだろうけども(笑)

------------------------
少し書き直したのがこちら

    Public Function webCapture(WebView2 As Microsoft.Web.WebView2.WinForms.WebView2) As Image
        Try
            Dim ms As New System.IO.MemoryStream() 'イメージ取得用のメモリーストリーム
            'WebView2の非同期画面キャプチャを実行
            Dim ts As Task = WebView2.CoreWebView2.CapturePreviewAsync(Microsoft.Web.WebView2.Core.CoreWebView2CapturePreviewImageFormat.Png, ms)
            'メモリーストリームに書き込み完了するまで待つ
            Do While ms.Length = 0
                System.Threading.Thread.Sleep(100) ' スリープ処理
                System.Windows.Forms.Application.DoEvents() ' イベントを処理させる
            Loop
            Return Image.FromStream(ms) ' 結果ストリームからイメージを習得
        Catch ex As Exception
        End Try
        Return Nothing ' イメージが取得できない
    End Function