~ 令和のIE ~
Caution
一般の方向けのツールではありません。
「なぜ動かないのか」ではなく、「どうやって動いているのか」を低レイヤーから理解したい方のための研究リポジトリ(POC)兼、備忘録です。
実務で安定したスクレイピングを行いたい方は、下記の定番ツールをご利用ください。
WebDriver(msedgedriver.exe / chromedriver.exe など)が担っている役割を、VBAだけでどこまで再現できるかを検証するPOCです。
外部ドライバ、SeleniumBasic、COM DLLの登録、管理者権限は一切使いません。
Excel VBA(標準モジュール/クラスモジュール)と Windows 標準API だけで、Chromium系ブラウザ(Edge / Chrome)や WebView2 と直接会話します。(実は Firefox も少しだけ)
リリースページから、ExcelかAccessを1つDLするだけで、追加のインストール/ダウンロードを必要としない、真のゼロ・インストールで試せます🦊
VBAでブラウザを自動操作していたIEの時代は、何の準備もなく、たったこれだけで自動化を始められました。
Dim ie As Object
Set ie = CreateObject("InternetExplorer.Application") '← この1行でブラウザが立ち上がる
ie.Visible = True
ie.Navigate "https://www.example.com/"技術的なフローは、これだけです。
flowchart LR
VBA["🟩 Excel VBA"] -- "COM" --> IE["🌐 Internet Explorer"]
IEのサポートが終了し、まず選択肢に入ったのが SeleniumBasic でした。
flowchart LR
VBA["🟩 Excel VBA"] -- "COM" --> SB["📦 SeleniumBasic<br/>(要インストール)"]
SB -- "HTTP<br/>(W3C WebDriver)" --> WD["⚙️ WebDriver.exe"]
WD -- "CDP" --> CR["🌐 Chromium"]
style SB stroke-dasharray: 5 5
style WD stroke-dasharray: 5 5
IEの頃と比べると、橋渡し役が 2つ も増えてしまいました🫠
その後、uezo氏(TinySeleniumVBA) や GCuser99氏(SeleniumVBA) により、VBAから直接 WebDriver へHTTPを投げる方式が登場。橋渡しが1枚削られ、現在に至ります。
flowchart LR
VBA["🟩 Excel VBA"] -- "HTTP<br/>(W3C WebDriver)" --> WD["⚙️ WebDriver.exe"]
WD -- "CDP" --> CR["🌐 Chromium"]
style WD stroke-dasharray: 5 5
SeleniumBasic はインストールが必要だったため、当初は確かに導入のハードルがありました。
しかし WebDriver.exe だけになった今は、ポータブルなexeを適切に配置し、決められたコマンドを打つだけで自動操作の準備が整います。
ある意味、ゼロインストールで自動操作できる時代になったと言えます。
そうです。IEの頃と比べると、まだ 1枚 橋渡し役が残っています。
このプロジェクトは、その 最後の橋渡し役を外す ためのPOCです。
flowchart LR
VBA["🟩 Excel VBA"] -- "❓❓❓" --> CR["🌐 Chromium"]
flowchart TB
subgraph E1["① IE時代(橋渡し 0枚)"]
direction LR
A1["VBA"] -->|COM| B1["IE"]
end
subgraph E2["② SeleniumBasic(橋渡し 2枚)"]
direction LR
A2["VBA"] -->|COM| S2["SeleniumBasic"] -->|HTTP| W2["WebDriver.exe"] -->|CDP| C2["Chromium"]
end
subgraph E3["③ TinySeleniumVBA / SeleniumVBA(橋渡し 1枚)"]
direction LR
A3["VBA"] -->|HTTP| W3["WebDriver.exe"] -->|CDP| C3["Chromium"]
end
subgraph E4["④ このPOC(橋渡し 0枚を目指す)"]
direction LR
A4["VBA"] -->|"???"| C4["Chromium"]
end
E1 --> E2 --> E3 --> E4
WebDriver.exe が最終的にブラウザと交わしているのは CDP(Chrome DevTools Protocol) という JSON-RPC 形式のメッセージです。
であれば、VBAが直接 CDP を話せば WebDriver.exe はいらない――というのが本POCの出発点です。
その「話し方(通信路)」として、現在は次の3つの経路を実装しています。
flowchart LR
VBA["🟩 Excel VBA<br/>(UTF-16LE)"]
subgraph Conv["文字コード変換"]
U8["UTF-16LE ⇄ UTF-8"]
end
VBA --> U8
U8 -- "🥇 匿名パイプ<br/>--remote-debugging-pipe<br/>(CreatePipe + CreateProcess)" --> CR1["🌐 Edge / Chrome"]
U8 -- "🥈 WebSocket(WinSock)<br/>--remote-debugging-port" --> CR2["🌐 Edge / Chrome<br/>(起動済み・別端末も可)"]
VBA -- "🥉 COM(vtable直呼び)<br/>CallDevToolsProtocolMethod(ForSession)" --> WV["🪟 WebView2<br/>(UserForm内)"]
| 経路 | 通信方式 | 主な実装 | ひとこと |
|---|---|---|---|
| 🥇 Pipe | --remote-debugging-pipe による匿名パイプ |
CDPCore.cls |
本POCの主軸。ポートを開かず、親子プロセス間だけで完結する |
| 🥈 Port | --remote-debugging-port + WinSock による WebSocket |
CDPCoreViaWebSocket.cls |
起動済みブラウザへの後付け接続や、別端末・Android実機への接続も可能 |
| 🥉 WebView2 | ICoreWebView2::CallDevToolsProtocolMethod(ForSession) |
CDPCoreViaWebView2.cls / WebView2Host.cls |
ポートもパイプも開かず、Excelのプロセス内でCDPをやり取りする |
どの経路でも、上位の CDPContext / CDPElement からは同じAPIで操作できるよう抽象化しています。
WebDriver.exe が裏でやっていることを、VBAが肩代わりしている様子です。
sequenceDiagram
autonumber
participant V as Excel VBA
participant K as Windows API<br/>(kernel32)
participant B as Edge / Chrome
V->>K: CreatePipe(CDP送信用・受信用のパイプなどを用意)
V->>K: CreateProcess<br/>--remote-debugging-pipe<br/>(STARTUPINFO.lpReserved2 でハンドルを fd3 / fd4 として継承)
K-->>B: ブラウザ起動
Note over V: VBA文字列(UTF-16LE) → UTF-8 変換
V->>B: WriteFile: {"id":1,"method":"Target.createTarget",...}\0
loop 応答待ち
V->>K: PeekNamedPipe(ブロックせずに覗き見)
end
B-->>V: ReadFile: {"id":1,"result":{...}}\0
Note over V: UTF-8 → UTF-16LE 変換 → JSONパース
V->>B: Target.attachToTarget / Page.navigate / Runtime.evaluate ...
B-->>V: 結果 & イベント(Page.loadEventFired など)
ポイントは次の3点です。
-
メッセージの区切りは NUL文字(
\0)。HTTPヘッダもWebSocketフレームもありません -
パイプのハンドルは、CRTのファイルディスクリプタ継承の仕組み(
lpReserved2)を使って子プロセスへ渡します -
VBA内部は UTF-16LE、CDP は UTF-8 なので、送受信のたびに相互変換が必要です
Microsoft 製の winhttp.dll にも WebSocket の API はあります。ただ、Pipe の PeekNamedPipe のような「届いているかだけ覗き見る」機能がありません。同期で待つと、届くまで Excel が固まります。非同期のコールバックを使うと、対策しない限り Excel ごと落ちます。
そこで、WebSocket は「TCP の上で決められたメッセージのルール」にすぎないので、WinSock(ws2_32.dll)の上で、WebSocket のルールを VBA で再現しています。
sequenceDiagram
autonumber
participant V as Excel VBA
participant S as WinSock<br/>(ws2_32)
participant B as Edge / Chrome
V->>B: Shell で起動<br/>--remote-debugging-port=0
B-->>V: DevToolsActivePort ファイルに<br/>空いているポート番号とパスを書き出す
V->>S: socket → connect(TCP接続)
V->>B: GET /devtools/browser/<ID><br/>(Upgrade: websocket)
B-->>V: HTTP/1.1 101 Switching Protocols
Note over V: VBA文字列(UTF-16LE) → UTF-8 変換し、<br/>マスク付きのテキストフレームに組み立てる
V->>B: send: フレームヘッダー + {"id":1,"method":...}
loop 応答待ち
V->>S: ioctlsocket(FIONREAD)<br/>(ブロックせずに受信量を覗き見)
end
B-->>V: recv: フレームヘッダー + {"id":1,"result":{...}}
Note over V: フレームヘッダーを外し、<br/>Pipe と同じ受信バッファーへ(終わりに \0 を足す)
ポイントは次の3点です。
-
接続先のポートとパスは、起動時に
--remote-debugging-port=0を付けて、Chromium が書き出すDevToolsActivePortファイルから読みます。起動済みのブラウザには、HTTP の/json/versionなどに問い合わせてつなぎます -
PeekNamedPipeの代わりは、ioctlsocket(FIONREAD)です。今どれだけ届いているかを、ブロックせずに確かめられます -
フレームの組み立て(FIN・opcode・長さ・マスク)と解析は、RFC 6455 に沿って VBA で手書きしています。取り出した中身は、Pipe と同じ
\0区切りの形にそろえるので、ここから先は Pipe 経路とまったく同じ処理です。しばらく何も届かないときは Ping を送り、その送信エラーで切断に気付くようにしています
Note
ハンドシェイクやフレームの詳細、DevToolsActivePort と「普段使いのブラウザへの接続」の関係は、技術資料: WebSocket 経路 にまとめています。
Excel には、WebView2 を扱う公式のコントロールがありません。
そこで、C++ のアプリなら WebView2 の SDK(コンパイラーと COM 用のライブラリ)が裏で用意してくれるものを、VBA で手作業で組み立てています。新しい通信方式を発明したわけではなく、WebView2 が裏でやっていることを再現しただけです。
sequenceDiagram
autonumber
participant V as Excel VBA
participant T as 自作のCOMオブジェクト<br/>(機械語サンク)
participant L as WebView2Loader.dll<br/>(Office同梱)
participant W as WebView2
Note over V,T: VirtualAlloc で確保したメモリに<br/>vtable と機械語サンクを書き込み、<br/>完了ハンドラ(COMオブジェクト)を自作
V->>L: CreateCoreWebView2EnvironmentWithOptions
L-->>T: Invoke(Environment の完成)
Note over T,V: どの Invoke も、サンクが VBA クラスの<br/>Handler_Invoke の呼び出しに詰め替えて届ける
V->>W: DispCallFunc で vtable[3] を呼ぶ<br/>CreateCoreWebView2Controller(UserForm の hWnd)
W-->>T: Invoke(Controller の完成)
V->>W: vtable[36] CallDevToolsProtocolMethod<br/>(セッション指定は vtable[99] の ForSession)
W-->>T: Invoke(errorCode, 結果のJson)
T->>V: 記録しておいたコマンドIDを足して渡す
Note over V: {"id":N,"result":...} に組み立て直し、<br/>Pipe と同じ受信バッファーへ
ポイントは次の3点です。
-
WebView2Loader.dllは、最近の Office(Microsoft 365 など)に最初から入っています。Declareで名前を書くだけで呼べるので、DLL を追加で入れる必要はありません -
COM のメソッドは名前で呼べないので、vtable の番号を数えて
DispCallFuncで呼んでいます。結果を受け取るコールバック用の COM オブジェクトも、メモリ上に直接組み立てています(WebView2Thunks.bas) -
WebView2 の結果には、CDP の
id(どのコマンドの結果か)やmethod(どのイベントか)が含まれません。そこで、ハンドラ側に記録しておいた値を足して、Pipe と同じ形の JSON に戻しています。ここから先は、Pipe 経路とまったく同じ処理です
Note
機械語サンク・vtable 呼び出しの心臓部は、WebView2-For-Excel-VBA(たーぼー氏)のロジックを移植したものです。
番号の数え方やコールバックの作り方の詳細は、技術資料: WebView2 経路 にまとめています。
W3C で標準化が進む次世代プロトコル WebDriver BiDi にも対応しています。
本来は WebDriver.exe 側が BiDi ⇔ CDP 翻訳ファイルをブラウザ内の専用タブに注入するのを担いますが、本POCでは Google 公式の chromium-bidi の Mapper(assset/mapperTab.js)をブラウザ内の専用タブに注入し、ブラウザ自身に翻訳させています。
flowchart LR
VBA["🟩 Excel VBA<br/>WebDriverBiDiCore"] -- "BiDiコマンド<br/>(CDP経由で onBidiMessage を呼ぶ)" --> MT
subgraph Browser["🌐 Edge / Chrome"]
MT["📄 Mapperタブ<br/>mapperTab.js<br/>(chromium-bidi)"] -- "CDP" --> PG["📄 操作対象のタブ"]
end
MT -- "sendBidiResponse<br/>(CDPイベントとして返却)" --> VBA
-
Mapperタブには
Target.exposeDevToolsProtocolで CDP の操作権を渡しています -
BiDi で足りない部分は、
WebDriverBiDiContext.UpgradeBiDiPlusにより同じタブを CDP 制御へ切り替えられます(BiDi+)
Chromium 向けに作ったこの仕組みですが、実は Firefox も同じコードで動かせます。
Chromium は BiDi を話せないので Mapper に翻訳させていました。一方 Firefox は、WebDriver BiDi をネイティブで話せます。
そこで翻訳機を外し、BiDi の JSON を WebSocket でそのまま Firefox に送っています。
flowchart LR
subgraph CR["Chromium(通常ルート)"]
direction LR
A1["🟩 WebDriverBiDiMode<br/>/ Context"] -->|"BiDi-Json"| A2["📄 BiDi-CDP Mapper<br/>(mapperTab.js)"]
A2 -->|"CDP-Json"| A3["🌐 Edge / Chrome"]
end
subgraph FF["Firefox(BiDi直結)"]
direction LR
B1["🟩 WebDriverBiDiMode<br/>/ Context"] -->|"BiDi-Json<br/>(WebSocket直)"| B2["🦊 Firefox"]
end
-
上側の
WebDriverBiDiMode/WebDriverBiDiContextは、Chromium のときと同じ使い方です(navigate/getTab/SubscribeBiDiEventなど) -
切り替えは、
ReattachOptions.Set_DirectWebDriverBiDi = Trueの 1プロパティだけです -
src/への追加は 3ファイルで81行追加・6行削除にとどまり、Firefox 専用のクラスは作っていません。Chromium 向けに積み上げてきた設計(責務分離とRaiseEvent、「BiDi で会話する側」としてのWebDriverBiDiMode、ReattachOptions)が、そのまま下地になりました
「Chromium で積み上げた設計が、本物の WebDriver BiDi にもそのまま通用した」という、ちょっとした答え合わせです。
Note
コア(src/)への追記が必要なため、メインの配布物には含めていません。
使い方・制約・コア側の追記内容は ex-Firefox ブランチの ReadMe にまとめています。
接続は WebSocket ルートのみで、BiDi+ や CDPContext / CDPElement など Chromium 固有の機能は使えません。
「WebDriverがいらない」と言っても、WebDriverがやっていた仕事が消えるわけではありません。VBA側で一つずつ肩代わりしています。
| WebDriver.exe の役割 | 本POCでの肩代わり先 |
|---|---|
| ブラウザプロセスの起動・引数組み立て | CDPHost.cls / CDPCore.cls(CreateProcess) |
| ブラウザとの通信路の確保 | 匿名パイプ / WinSock / WebView2 COM |
| メッセージのシリアライズ | 独自の高速JSONパーサー(BiDiCDPJson.cls など) |
| 文字コードの扱い | CharacterCodeConversion.cls(UTF-16LE ⇄ UTF-8) |
| セッション・タブ(Target)の管理 | CDPBrowser.cls / CDPContext.cls |
| 要素検索・クリック・入力 | CDPElement.cls(DOM / Runtime / Input ドメイン) |
| イベントの購読 | CDPイベント / BiDiイベントのキューイング |
| W3C WebDriver BiDi 対応 | chromium-bidi Mapper の注入(WebDriverBiDiCore.cls) |
| ブラウザとドライバのバージョン整合 | 不要(ブラウザ本体の CDP を直接使うため) |
| 引数名 | 意味 |
|---|---|
remote-debugging-pipe / remote-debugging-port |
外部プロセス(Excel)からのデバッグ接続を受け付けさせます。pipe は同一PCの親子プロセス間でのみ有効です。port は 0(空きポートの自動割り当て)で起動し、接続先は DevToolsActivePort ファイルから取得します |
user-data-dir |
プロファイルの保存先。デバッグ機能を悪用したCookie盗難対策により、既定の User Data 以外を指定する必要があります。既定では同階層に Automation Data を作成します |
homepage |
余計な通信を抑えるため about:blank を指定します |
app |
最初のURLを指定した場合に付与。タブ生成やURL変更をある程度防げます(簡易キオスクモード) |
設定シート側で付与している引数(no-first-run, disable-sync, enable-automation など)や、より詳しい仕組みは 公式ドキュメント にまとめています。
POCとしての意味を保つため、次のルールを自らに課しています。
- SeleniumBasic や外部COM DLLの登録を前提としない
msedgedriver.exe/chromedriver.exeなどの外部バイナリを配置・要求しない- 管理者権限が必要なレジストリ操作やインストーラーを実行しない
- Python / PowerShell などの別スクリプトを生成・実行しない(VBA単体で完結)
設定シート(ブラウザ起動設定)の内容でブラウザを起動し、ページを開いて閉じるだけの例です。
Sub CDPによる冒険の始まり()
Dim t As CDPContext
Set t = ShSetting01_StartBrowser.StartCDPModeContext '既定は Pipe 経路
t.navigate "https://www.example.com/"
t.ThisCDPBrowser.quit
End SubSub BiDiによる冒険の始まり()
Dim t As WebDriverBiDiContext
Set t = ShSetting01_StartBrowser.StartBiDiModeContext
'↓ここに処理を書く↓
t.ThisWebDriverBiDiMode.quit
End Sub-
Port経路は、設定シートの「WebSocketモード」をONにするだけで切り替わります(コードの変更は不要)
-
起動済みブラウザへのアタッチは
Demo_CDP、WebView2 はDemo_WebView2に同梱デモがあります
Note
ダウンロードしたブックを開くと「保護ビュー」が表示され、マクロが動かないことがあります。
Excelを閉じてからファイルを右クリック →「プロパティ」→「許可する」にチェックを入れて OK を押してください。(Mark of the Web の解除)
API リファレンス、アーキテクチャ、開発秘話などは GitHub Pages にまとめています。
➡️ 公式ドキュメント
冒頭で紹介したCreateObject("InternetExplorer.Application")は、あくまでIEを新しく起動するための方法です。
実は、IEではほかに次の2つもできました。
この2つは、プロジェクトを立ち上げた当初には実現できていませんでした。
しかし、AIが発展したことで、どちらも実現できてしまいました。
- 起動済みChrome/Edgeの制御:AIによるコーディングエージェントのために
- こちらはブラウザ側の公式対応です。AIが発展したからこそ対応された機能なのかもしれません。
- UserFormへのWebView2の埋め込み:AIがWebView2の裏側を組んでくれた
- 人間には到底できない超低レイヤーの土台を、AIが組んでくれました。
前者は、AIの発展を受けて、企業が公式に対応してしまいました。
後者は、AIの発展のおかげで、非公式ながらUserFormへのWebView2の埋め込みに成功しました。
AIって、ある意味恐ろしいものですね。
一般的なWebスクレイピングやブラウザ自動化が目的であれば、成熟した次のツールを強くおすすめします。
| ツール | 言語 | ひとこと |
|---|---|---|
| Playwright | Node.js / Python / .NET / Java | 現代の定番。複数ブラウザ対応、待機処理も賢い |
| Puppeteer | Node.js | CDP 直結の元祖的存在。本POCの発想の源のひとつ |
| Selenium | 各種 | W3C WebDriver の本家 |
| TinySeleniumVBA | VBA | 軽量。VBAから WebDriver を直接叩く方式の先駆け |
| SeleniumVBA | VBA | 高機能。VBAでの実務利用ならまずこちら |
| WebDriver-BiDi-for-SeleniumVBA | VBA | SeleniumVBAの基盤にWebDriverBiDiプロトコル制御を拡張した物。特に、SPAサイトへの攻略に重宝します |
本リポジトリは、あくまで「最後の橋渡しを外したらどうなるか」を確かめるための研究リポジトリという立ち位置です。
このPOCは、世界中のVBA職人や開発チームが公開してくれた素晴らしいライブラリの数々の上に成り立っています。
偉大な先人たちの知恵とコードに、心からの敬意と感謝を表します。
-
WebSocket実装のコアロジック
-
CDP制御・パイプ通信の基盤
-
Chromium-Automation-with-CDP-for-VBA
- 製作者:longvh211氏
-
-
WinHTTP 5.1 ラッパー
-
- オリジナル製作者:Tim Hall氏
-
-
CDP/WebDriverBiDiレスポンス専用の超高速JSONパーサー
-
-
製作者:Ueslei Paim
-
CopyMemory不使用版に改良
-
-
-
VBA-JSONの上位互換の高速JSONパーサー-
- 製作者:Cristian Buse
-
-
Microsoft製
Scripting.Dictionaryの上位互換の高速Dictionary-
- 製作者:Cristian Buse
-
-
高速な文字コード変換ラッパー
-
How to convert VBA/VB6 Unicode strings to UTF-8
- David Ireland DI Management Services Pty
-
VBAで Windows APIを使った UTF-8 ←→ Unicode相互変換
- 製作者:@yamashiroakihito
-
-
ログレベルの基礎部分
-
- 製作者:timhall氏
-
-
ChromiumブラウザをBiDi化するためのコアロジック
-
- 製作者:GoogleChromeLabsチーム
-
-
UserFormにWebView2を追加DLなしで埋め込んだすごい方
-
「VBAから直接WebDriverへ」の道を切り拓いた方々
-
- 製作者:uezo氏
-
- 製作者:GCuser99氏
-
このリポジトリは教育目的のみで提供されています。
いかなる目的においても、正確性、完全性、適合性についての保証は一切提供されません。使用は自己責任で。
作者は、このコードの使用、改変、配布に伴う損害、法的問題、保証違反について一切の責任を負いません。
法的違反等はユーザーの責任です。

