Skip to content

Excel単独で「真のWebView2」を完全制御する ​

さぁ、ここが本当の目的。
Excelだけで、しかもPowershellのような外部source依存もなしで、真WebView2をUserformに。

ついに辿り着きました。これが本プロジェクトの真の目的であり、到達点です。
外部プロセス(PowerShellなど)に頼らず、Excel VBAのメモリ空間上だけで WebView2 を直接起動・制御します。

v3.0.0でネイティブ実装済み

以前このページは「理論上は可能だが本キット未実装、詳しくは他プロジェクトへ」という紹介記事でした。v3.0.0で、この技法を本キット自身に CDPCoreViaWebView2.cls / WebView2Thunks.bas(v3.0.0時点ではCDPWebView2Thunks.bas、v3.1.0で改名) / WebView2Form.frm として本実装しています。接続後は、Pipe / WebSocket と同じ CDPContext / CDPElement の API でそのまま操作できます。実際の使い方はこのページの後半を参照してください。

一見するとLv.1(Edge埋め込み)と似ていますが、タスクマネージャーを見ればその違いは一目瞭然です。

Excel直からWebView2

▲ Excel.exe の配下に直接 WebView2 プロセスが生成されています

禁断の魔導:VBAによるCOM直接制御の仕組み ​

なぜ、これまでこれが不可能だと思われていたのか。それは WebView2 が IUnknown ベース という、VBAにとっては非常に「扱いづらい」設計になっているからです。

INFO

通常、VBAで Object として扱えるものは IDispatch という「親切な案内板」を持っています。しかし、WebView2にはそれがありません。

1. DispCallFunc による関数の強行突破 ​

「オブジェクト.メソッド」という通常の呼び出しができないため、Windows APIの DispCallFunc を使用します。
これは、メモリ上の「関数の住所(vtableのインデックス)」を直接指定して実行する、いわば VBA界の狙撃術です。

2. vtable 偽造:自作のCOMオブジェクトを作る ​

WebView2は、処理が終わると「終わったよ!」とコールバック(通知)を返してきます。この通知を受け取るには「WebView2が理解できる形式のオブジェクト」である必要があります。
VBAの AddressOf で取得した関数のポインタを構造体に詰め込み、メモリ上に「COMオブジェクトのフリをしたデータ」を構築(vtable偽造)することで、WebView2からの通信を直接受け止めます。

処理のリレー(非同期通信)

VBAが制御を投げる → WebView2が処理 → 偽造オブジェクト経由でVBAのハンドラを叩く → VBAが次の指示を出す……という、極めて高度な連携によって動作しています。

実装のエッセンス ​

vtable構築の核心部分
vb
' COM vtable 偽造オブジェクトの定義
Private Type VtblObj
    pVTable As LongPtr
End Type

' 4つのエントリ(QI, AddRef, Release, Invoke)を持つvtable
Private Type VtblData4
    fn(0 To 3) As LongPtr
End Type

' 実行時にVBA標準モジュールのメソッドを AddressOf で登録
Private Sub BuildVtables()
    Dim envFn()  As LongPtr
    WV2_FillFunctionPointers envFn

    m_EnvHandlerVT.fn(0) = envFn(0) ' QueryInterface
    m_EnvHandlerVT.fn(1) = envFn(1) ' AddRef
    m_EnvHandlerVT.fn(2) = envFn(2) ' Release
    m_EnvHandlerVT.fn(3) = envFn(3) ' Invoke (本命のコールバック)

    ' これがWebView2側に渡す「偽オブジェクト」のポインタになる
    m_EnvHandlerThis.pVTable = VarPtr(m_EnvHandlerVT.fn(0))
End Sub

正直に申し上げて、これは VBAの限界を突破した「ハック」 に近いです。
しかし、この手法をマスターすれば、TLBや外部DLLに一切頼ることなく、最新のブラウザエンジンをExcelのUserFormに完全に支配下に置くことができます。

移植元

この機械語サンク・vtable呼び出し・SAFEARRAYメモリプリミティブの心臓部は、WebView2-For-Excel-VBA(Wv2Thunks.bas)のロジックを、バイト列やオフセット値を一切変更せずそのまま移植したものです。本キット向けに追加したのは、CDP用のハンドラ種別(CallDevToolsProtocolMethodForSessionAsync / DevToolsProtocolEventReceivedEventHandler のIID登録含む)と、v3.1.0で追加した拡張機能対応(EnvironmentOptionsエミュレーション)用のget_/put_ハンドラ群です。

本キットでの使い方 ​

WebView2Form.frm の StartCDPModeWebView2 を呼ぶだけで、初期化からCDP接続まで完了します。

vb
Sub WebView2OnExcelUserForm()
    '1. UserForm側のWebView2の初期化を済ませる
    With WebView2Form
        .StartCDPModeWebView2

        '2. あとはいつも通りの CDPContext / CDPElement 操作
        .ThisCDPContext.navigate "https://github.com/Eschamali/StarterWebScrapingKit"

        '3. フォームを表示
        .show
    End With
End Sub

同梱デモ: Demo_WebView2.WebView2OnExcelUserForm

内部では CDPCoreViaWebView2.OpenWebView2AndConnectCDP がWebView2のEnvironment/Controller/ICoreWebView2を生成し、ReattachOptions.Set_WebView2Mode と CDPBrowser.reattach を通じて、Pipe版・Port版とまったく同じCDPスタックに接続します。つまり、いったん埋め込んでしまえば、getElementByQuery や jsEval など、これまでのガイドで説明してきた操作がそのまま使えます。

自前のUserFormに組み込む場合のAPI詳細(OpenWebView2AndConnectCDPの引数、イベント購読、リサイズ等)は WebView2モードでできること にまとめています。

リセットに注意(静止ブレークポイント自体は問題なし)

全てのCOMコールバックは機械語サンクを経由します。同期メソッドの呼び出し中そのものでブレークする、あるいは静止ブレイク中にCDPイベント/コマンド完了が届くと、クラッシュの可能性があります。そのいずれの状態でも、VBEでリセット操作をすると、ほぼ確実にExcelがクラッシュします。普通のVBAコード行で静止しているだけなら問題ありません。デバッグ時、リセットする前にはイミディエイトウィンドウからUnsubscribeAllCdpEventsで購読解除しておいてください(ただし、これは内部トラッキング用の自動購読分も解除するため、デバッグ後は再接続が必要です)。詳細な条件分けは設計思想についてを参照してください。

WARNING

より低レベルな仕組みの探求の覚悟があるなら、移植元のリポジトリも覗いてみてください:
WebView2-For-Excel-VBA (GitHub)

最後に、このロマンな技術テクニックを公開、教えてくれた「たーぼー(インコ)」さんに感謝します。

次へ ​

Excel VBA × CDP / WebDriver BiDi