レガシーの呪縛を解き放て:WebBrowserからWebView2へ、WinFormsモダンUI変革の極意
諸君、今日のテーマは、単なる技術的な移行に留まらない。それは、長年WinFormsアプリケーションの足枷となってきた『レガシーの呪縛』を解き放ち、業務効率化ツールのUIを現代の要請に応える形で覚醒させるための、極めて重要な変革だ。
WebBrowserコントロール。かつては画期的だったこのコンポーネントが、今や我々の開発現場において、パフォーマンス、セキュリティ、そして何よりも「モダンさ」のボトルネックとなっていることは明白だろう。Internet Explorerベースという宿命を背負ったこのコントロールは、もはや現代のWeb標準に追従できない。その結果、表示崩れ、JavaScriptの非互換性、脆弱性、そして何よりユーザー体験の陳腐化という、看過できない課題を我々に突きつけてきた。
しかし、諸君、恐れることはない。マイクロソフトは、この課題に対する明確な解答を用意してくれた。それが、本日深掘りするWebView2コントロールだ。
WebView2とは何か:Chromiumの鼓動がWinFormsに宿る
WebView2は、Microsoft Edgeの基盤となっているChromiumレンダリングエンジンをWinFormsアプリケーションに組み込むためのコントロールだ。これまでのWebBrowserコントロールがIEの亡霊を彷徨わせていたのに対し、WebView2は最新のChromiumの鼓動をダイレクトにアプリケーションに注入する。
この変化が意味することは計り知れない。
- 最新のWeb標準への完全準拠: HTML5、CSS3、モダンJavaScriptフレームワーク(React, Vue, Angularなど)が、WinFormsアプリ内で何の障壁もなく動作する。
- 圧倒的なパフォーマンス: Chromiumエンジンがもたらす高速なレンダリングとJavaScript実行性能は、IEベースの比ではない。ユーザーはサクサク動くUIに驚くだろう。
- 堅牢なセキュリティ: 最新のセキュリティ機能が常にアップデートされ、Webの脅威からアプリケーションとユーザーを保護する。
- 開発者体験の向上: EdgeのF12開発者ツールがそのまま利用可能となり、Webコンテンツのデバッグが格段に容易になる。
単なるWebコンテンツの表示機能の強化ではない。これは、WinFormsアプリケーションが、もはや「デスクトップアプリ」と「Webアプリ」の間の境界線を意識することなく、両者の利点を融合させた「ハイブリッドアプリケーション」へと進化するための扉を開くものだ。
移行への第一歩:準備とセットアップの極意
さて、この強力なツールを使いこなすには、その導入における「極意」を知る必要がある。表面的な手順に惑わされてはならない。
1. 前提条件の確認とNuGetパッケージのインストール
まず、プロジェクトのターゲットフレームワークを確認しよう。WebView2は、.NET Framework 4.6.2以降、または.NET Core 3.1以降をサポートする。既存のWinFormsプロジェクトがこれより古い場合は、まずフレームワークのアップデートを検討すべきだ。
次に、Visual StudioのNuGetパッケージマネージャーを開き、「Microsoft.Web.WebView2」を検索し、インストールする。
バージョンは常に最新安定版を使用することを推奨する。
2. WebView2ランタイムの扱い:配布戦略の肝
ここが、WebBrowserコントロールとの根本的な違いであり、業務アプリケーション開発者として最も注意すべき点だ。WebView2コントロールは、アプリケーションとは別に「WebView2 Runtime」が必要となる。このランタイムの配布戦略が、アプリケーションの堅牢性と保守性を左右する。
主に以下の2つの選択肢がある。
- Evergreen Runtime (推奨):
- ユーザーのシステムにインストールされているMicrosoft Edge (Chromium) のコンポーネントを利用する。
- メリット: アプリケーションのサイズが小さく、ランタイムのセキュリティアップデートや機能改善が自動的に行われる。
- デメリット: ユーザーのシステムにEdgeがインストールされているか、またはWebView2 Runtimeが別途インストールされている必要がある。
- 業務ツールとしての設計思想: 組織内のPCにはEdgeが導入されている場合が多いため、これが最も保守が容易で推奨される。導入時にランタイムの有無を確認し、なければダウンロードを促す機能を持たせるのが親切だ。
- Fixed Version Runtime:
- アプリケーションのパッケージ内に特定のバージョンのWebView2 Runtimeをバンドルする。
- メリット: アプリケーションが完全に自己完結し、ランタイムの依存関係がない。オフライン環境でも確実に動作する。
- デメリット: アプリケーションのサイズが非常に大きくなる。ランタイムのセキュリティアップデートやバグフィックスは、アプリケーション自体をアップデートしない限り反映されない。
- 業務ツールとしての設計思想: 特定の環境に閉じられた、バージョン固定が必須なシステムや、インターネット接続が制限される環境でのみ検討すべきだ。保守コストは非常に高くなることを覚悟せよ。
我々が目指すのは「バグの起きない堅牢な設計」と「保守性の高いプロダクションコード」だ。故に、基本的にはEvergreen Runtimeを前提とし、アプリケーション起動時にランタイムの初期化とチェックを行う設計を強く推奨する。
.net
Imports Microsoft.Web.WebView2.Core
Imports Microsoft.Web.WebView2.WinForms
Imports System.IO
Public Class MainForm
Private Async Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘ WebView2コントロールの初期化は非同期で行う必要がある
‘ CoreWebView2_Ready イベントで初期化完了を待つ設計も可能だが、
‘ ロード時に待機する方がシンプルで堅牢
Try
‘ WebView2 Runtimeのパスを指定(Evergreenの場合、通常はシステムにインストールされているものが使われる)
‘ ただし、特定のバージョンや開発環境でのテスト用にパスを指定することもできる
Dim environment As CoreWebView2Environment = Nothing
Try
‘ WebView2 Runtimeの有無を確認し、もしなければインストールを促す
‘ これは開発環境やテスト環境で特に重要
environment = await CoreWebView2Environment.CreateAsync(Nothing, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, “WebView2UserFolder”))
Catch ex As Exception
‘ WebView2 Runtimeが見つからない、または初期化に失敗した場合
MessageBox.Show($”WebView2 Runtimeの初期化に失敗しました。{vbCrLf}” &
$”最新のMicrosoft Edge WebView2 Runtimeがインストールされていることを確認してください。{vbCrLf}” &
$”エラー詳細: {ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
‘ アプリケーションを終了するか、WebView2関連の機能を無効にするなどの対処
Me.Close()
Return
End Try
‘ WebView2コントロールの初期化を待つ
‘ これにより、CoreWebView2プロパティが確実に初期化される
await WebView21.EnsureCoreWebView2Async(environment)
‘ 初期化が完了したら、任意のURLをナビゲート
If WebView21.CoreWebView2 IsNot Nothing Then
WebView21.CoreWebView2.Navigate(“https://www.google.com”)
Else
MessageBox.Show(“WebView2コントロールの初期化に失敗しました。”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
End If
Catch ex As Exception
MessageBox.Show($”予期せぬエラーが発生しました: {ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
End Try
End Sub
‘ アプリケーション終了時にリソースを適切に解放する
Private Sub MainForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
If WebView21 IsNot Nothing AndAlso WebView21.CoreWebView2 IsNot Nothing Then
WebView21.Dispose() ‘ WebView2コントロールのリソースを解放
End If
End Sub
End Class
上記のコードでは、`EnsureCoreWebView2Async`を`Await`で待機している。これにより、`CoreWebView2`プロパティが`Nothing`になるという、WebView2初心者が陥りがちな罠を回避できる。また、エラーハンドリングを導入し、ランタイムが見つからない場合の挙動を明確にしている。リソースの解放(`Dispose`)も忘れてはならない。オブジェクトのライフサイクルを意識した堅牢な設計だ。
WebBrowserからWebView2へのコード移行:断絶と再構築
WebBrowserからの移行は、単なるプロパティ名の変更ではない。それは、イベント駆動モデルの進化と、非同期処理の徹底を意味する。
1. 基本的なコントロール操作の変革
| WebBrowser (IE) | WebView2 (Chromium) | 備考 |
| :———————————– | :————————————— | :————————————————————————————————————————————————————————————————————————————- |
| `Navigate(url)` | `CoreWebView2.Navigate(url)` | `CoreWebView2`プロパティが`Nothing`でないことを確認後に呼び出す。 |
| `DocumentText = htmlString` | `CoreWebView2.NavigateToString(htmlString)` | ローカルのHTML文字列を表示する場合。 |
| `Document.InvokeScript()` | `CoreWebView2.ExecuteScriptAsync()` | 非同期メソッド。JavaScriptの実行結果を`String`で受け取る。JSON文字列として結果を返すのが一般的。 |
| `ScriptErrorsSuppressed` | `CoreWebView2.Settings.IsScriptEnabled` | WebView2では、JavaScriptエラーは通常コンソールに出力される。`IsScriptEnabled`はJSの有効/無効を切り替える。 |
| `Document`オブジェクトへの直接アクセス | 推奨されない | WebView2はWinForms側からDOMを直接操作するAPIを提供しない。JavaScriptを介してDOMを操作する設計に切り替えるべき。これが「モダンWeb」との連携だ。 |
2. イベントモデルの進化:非同期処理の徹底
WebBrowserの`DocumentCompleted`イベントは同期的に発火していたが、WebView2では非同期処理が基本となる。
- `CoreWebView2_NavigationCompleted`: Webページのナビゲーションが完了したときに発火する。WebBrowserの`DocumentCompleted`に相当するが、`IsSuccess`プロパティで成否を判断し、エラー時には適切な処理を行うべきだ。
- `CoreWebView2_WebMessageReceived`: JavaScriptからWinFormsにメッセージが送信されたときに発火する。これこそが、双方向通信の要となる。
3. JavaScriptとの連携:双方向通信の真髄
ここがWebView2の最も強力な点であり、業務ツール開発の可能性を飛躍的に広げる領域だ。VB.NETとJavaScript間でのシームレスなデータ交換を実現する方法を徹底的に解説する。
a. VB.NETからJavaScriptを呼び出す (`ExecuteScriptAsync`)
これは、WebView2内のWebページで特定のJavaScript関数を実行したり、変数を設定したりする際に使用する。
.net
‘ 例: Webページ内のJavaScript関数を呼び出し、結果を受け取る
Private Async Sub CallJavaScriptButton_Click(sender As Object, e As EventArgs) Handles CallJavaScriptButton.Click
If WebView21.CoreWebView2 IsNot Nothing Then
‘ JavaScriptで定義された関数を呼び出す
‘ 例: function greet(name) { return “Hello, ” + name + “!”; }
Dim scriptResult As String = await WebView21.CoreWebView2.ExecuteScriptAsync(“greet(‘WinForms User’)”)
‘ スクリプトの結果はJSON文字列として返されることが多い
‘ JavaScript側でJSON.stringify()を使っていれば、VB.NET側でJSON.Parse()する
MessageBox.Show($”JavaScriptからの応答: {scriptResult}”, “JavaScript実行結果”)
‘ 例: Webページ内の要素のテキストを変更する
await WebView21.CoreWebView2.ExecuteScriptAsync(“document.getElementById(‘messageArea’).innerText = ‘VB.NETから設定されたメッセージ’;”)
End If
End Sub
b. VB.NETからJavaScriptへデータを送る (`PostWebMessageAsString`/`PostWebMessageAsJson`)
WinFormsアプリの内部データ(データベースから取得した情報など)をWebページに渡し、表示や処理に利用させたい場合に使う。JSON形式でのデータ送信を強く推奨する。構造化されたデータを扱う上で、文字列よりもはるかに堅牢だからだ。
.net
‘ 例: VB.NETからJavaScriptへユーザー情報をJSON形式で送信
Private Async Sub SendDataToJsButton_Click(sender As Object, e As EventArgs) Handles SendDataToJsButton.Click
If WebView21.CoreWebView2 IsNot Nothing Then
Dim userData As New With {
.Name = “田中 太郎”,
.Age = 35,
.Email = “taro.tanaka@example.com”,
.Timestamp = DateTime.Now.ToString(“yyyy-MM-dd HH:mm:ss”)
}
‘ JSONシリアライザー(Newtonsoft.Jsonなど)を使ってオブジェクトをJSON文字列に変換
Dim jsonString As String = Newtonsoft.Json.JsonConvert.SerializeObject(userData)
‘ JavaScript側で ‘message’ イベントリスナーがこのメッセージを受け取る
WebView21.CoreWebView2.PostWebMessageAsJson(jsonString)
MessageBox.Show(“VB.NETからJavaScriptへデータを送信しました。”, “送信完了”)
End If
End Sub
c. JavaScriptからVB.NETへデータを送る (`WebMessageReceived`イベント)
WebView2内のWebページで行われたユーザー操作(ボタンクリック、フォーム送信など)の結果や、Webページの状態変化をWinFormsアプリに通知したい場合に使う。
.net
‘ JavaScriptからのメッセージを受信するイベントハンドラー
Private Sub WebView21_CoreWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs) Handles WebView21.CoreWebView2.WebMessageReceived
‘ JavaScriptから送信されたメッセージを取得
Dim message As String = e.WebMessageAsJson ‘ または e.WebMessageAsString
‘ 受信したJSONメッセージをVB.NETオブジェクトにデシリアライズ
Try
‘ 例: JavaScriptから { “action”: “saveData”, “data”: { “id”: 123, “value”: “some value” } } のようなJSONが送られてくる
Dim receivedData = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(message)
Dim action As String = receivedData(“action”).ToString()
Select Case action
Case “saveData”
‘ データベースに保存する処理などを実行
Dim dataToSave = receivedData(“data”)
MessageBox.Show($”JavaScriptから ‘saveData’ アクションを受信しました。データ: {dataToSave.ToString()}”, “受信完了”)
‘ ここでデータベース連携などのVB.NET側の処理を実行する
SaveDataToDatabase(dataToSave)
Case “showInfo”
‘ UIに情報を表示するなどの処理
Dim infoMessage As String = receivedData(“message”).ToString()
MessageBox.Show($”JavaScriptから情報を受信しました: {infoMessage}”, “情報”)
Case Else
MessageBox.Show($”未知のアクション: {action}”, “エラー”)
End Select
Catch ex As Exception
MessageBox.Show($”JavaScriptからのメッセージ解析中にエラーが発生しました: {ex.Message}{vbCrLf}メッセージ: {message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Sub SaveDataToDatabase(data As Object)
‘ ここにデータベースへの保存処理の実装
‘ 例: Console.WriteLine($”データをデータベースに保存: {data.ToString()}”)
End Sub
d. 堅牢な通信設計の原則
1. メッセージングプロトコルの定義:
- VB.NETとJavaScript間でやり取りするメッセージの構造を明確に定義せよ。JSON形式を標準とし、必ず`action`や`type`などの識別子を含め、データのスキーマを固定する。
- 例: `{ “action”: “commandName”, “payload”: { / データ / } }`
2. エラーハンドリング:
- JavaScript側からのメッセージは常に検証し、期待する形式と異なる場合はエラーとして扱う。`Try-Catch`ブロックを徹底せよ。
- VB.NET側からの`ExecuteScriptAsync`も、JavaScript側のエラーを捕捉できるよう、Promiseを使った設計を検討する。
3. セキュリティ上の注意点:
- JavaScriptからVB.NETに渡されるデータは、信頼できない外部からの入力と見なせ。データベースクエリやファイルパスに直接使用せず、必ず入力値検証(サニタイズ)を行え。SQLインジェクションやパスインジェクションのリスクを排除せよ。
- XSS (Cross-Site Scripting) 対策として、Webコンテンツに外部の信頼できないスクリプトを読み込まない、ユーザー入力をエスケープして表示するなど、Web開発の基本原則を適用せよ。
実践的設計パターン:業務効率化ツールを支える堅牢なアーキテクチャ
業務効率化ツールでは、ファイルやデータベースとの連携が不可欠だ。WebView2とこれらのリソースを連携させる際の「極限の知見」を共有しよう。
1. ファイル連携の注意点:セキュリティとアクセス制御
WebView2はChromiumベースであるため、通常のWebブラウザと同様に、セキュリティ上の理由からローカルファイルシステムへの直接アクセスは厳しく制限されている。これは賢明な設計であり、我々はこの制約の中で安全な連携方法を構築する必要がある。
- ローカルファイルの表示:
- `CoreWebView2.Navigate(“file:///C:/path/to/local.html”)` のように直接パスを指定することも可能だが、セキュリティリスクを伴うため推奨されない。
- より安全な方法は、`CoreWebView2.SetVirtualHostNameToFolderMapping` を使用して、特定のローカルフォルダを仮想ホストにマッピングすることだ。これにより、WebコンテンツはあたかもWebサーバーから配信されているかのようにローカルファイルにアクセスできる。
.net
‘ 例: ローカルフォルダを仮想ホストにマッピング
Private Async Sub InitializeWebView2WithLocalContent()
If WebView21.CoreWebView2 Is Nothing Then
Await WebView21.EnsureCoreWebView2Async()
End If
If WebView21.CoreWebView2 IsNot Nothing Then
Dim localContentPath As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, “WebResources”) ‘ アプリ実行フォルダ内のWebResourcesフォルダ
Dim virtualHostName As String = “myapp.local” ‘ 任意の仮想ホスト名
‘ 仮想ホストを登録
WebView21.CoreWebView2.SetVirtualHostNameToFolderMapping(virtualHostName, localContentPath, CoreWebView2HostResourceAccessKind.Allow)
‘ 仮想ホストを介してローカルHTMLファイルにアクセス
WebView21.CoreWebView2.Navigate($”https://{virtualHostName}/index.html”)
End If
End Sub
これにより、Webコンテンツは `https://myapp.local/index.html` のようにアクセスでき、ローカルファイルへの直接パスを公開するリスクを回避できる。
- ユーザーによるファイル選択/保存:
- Webページ上でファイル選択ダイアログを表示したい場合は、JavaScriptからWinForms側にメッセージを送り、WinForms側で`OpenFileDialog`や`SaveFileDialog`を起動し、選択されたファイルの情報をJavaScriptに返す、という連携が最も安全かつ一般的だ。
- ダウンロード処理は`CoreWebView2_DownloadStarting`イベントでハンドリングできる。ここではダウンロードをキャンセルしたり、保存先を変更したりできる。
2. データベース連携の注意点:APIサーバー経由の鉄則
WebView2からデータベースに直接接続することは、絶対に避けろ。 これはセキュリティの観点からも、保守性の観点からも、最もやってはいけないアンチパターンだ。
Webコンテンツは本質的にクライアントサイドであり、信頼できない可能性がある。データベースの接続文字列や認証情報をWebコンテンツに露出させることは、重大なセキュリティホールとなる。
- 推奨されるアーキテクチャ:
- WinFormsアプリを「APIサーバー」と見なせ。
- WebView2内のWebコンテンツは、JavaScriptの`fetch`APIなどを使って、WinFormsアプリに対してメッセージを送信する(前述の`PostWebMessageAsJson`)。
- WinFormsアプリは、このメッセージを受信し、データベースへのアクセス、データの取得、更新、保存などの処理を安全に実行する。
- 処理結果は、再度`PostWebMessageAsJson`や`ExecuteScriptAsync`を使ってJavaScriptに返す。
このアーキテクチャにより、データベースへのアクセスロジックと認証情報はWinFormsアプリ内に完全にカプセル化され、Webコンテンツは安全な「窓口」を通じてのみデータにアクセスできる。
3. 長期運用を見据えた設計:リソース、ログ、バージョンアップ
- リソースの解放 (`Dispose`):
- WebView2コントロールは、必ず`Form.FormClosed`イベントなどで`WebView21.Dispose()`を呼び出し、リソースを適切に解放すること。特に`CoreWebView2`オブジェクトは、多くのシステムリソースを消費する。
- これにより、メモリリークやハンドルリークを防ぎ、アプリケーションの安定稼働を保証する。
- エラーログの収集と活用:
- WebView2の各イベント(`NavigationCompleted`のエラー、`WebResourceRequested`のエラーなど)や、JavaScriptからのメッセージ処理におけるエラーは、必ずログに出力する設計にせよ。
- これにより、ユーザー環境で発生した問題を迅速に特定し、修正することが可能となる。
- バージョンアップへの対応戦略:
- Evergreen Runtimeを使用している場合、ランタイムの自動更新により、Webコンテンツが予期せぬ動作をする可能性もゼロではない。定期的なテストを計画に含めること。
- Fixed Version Runtimeの場合は、セキュリティアップデートやバグ修正のために、アプリケーション自体を定期的に再配布する必要がある。このコストを考慮せよ。
コピペで動くプロダクションコード例:実践の羅針盤
それでは、WinFormsアプリケーション内にWebView2を組み込み、VB.NETとJavaScript間で双方向通信を行う具体的なコード例を示す。このコードは、そのままあなたの業務効率化ツールの基盤となり得る。
前提:
1. Visual Studioで新しいWinForms (.NET Framework or .NET Core) プロジェクトを作成。
2. NuGetで`Microsoft.Web.WebView2`と`Newtonsoft.Json`をインストール。
3. `Form1`に`WebView2`コントロールと、通信テスト用の`Button`をいくつか配置。
WinForms (VB.NET) コード
.net
Imports Microsoft.Web.WebView2.Core
Imports Microsoft.Web.WebView2.WinForms
Imports System.IO
Imports Newtonsoft.Json ‘ JSON処理のためにNewtonsoft.Jsonを使用
Public Class MainForm
‘ WebView2コントロールの名前が ‘WebView21’ であると仮定
‘ フォームデザイナーで配置した場合、通常この名前になる
Private Async Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Text = “WebView2 双方向通信デモ”
Await InitializeWebView2()
End Sub
Private Async Function InitializeWebView2() As Task
Try
‘ WebView2 Runtimeの初期化環境を構築
‘ ユーザーデータフォルダは、アプリケーションごとにユニークな場所を指定することを推奨
‘ これにより、複数のWebView2アプリが同じプロファイルを使用しないようにする
Dim userDataFolder As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), “YourAppName”, “WebView2UserData”)
Directory.CreateDirectory(userDataFolder) ‘ フォルダが存在しない場合は作成
Dim env As CoreWebView2Environment = Nothing
Try
‘ CreateAsyncの第一引数はRuntimeのパス。NothingでEvergreen Runtimeを探す
env = await CoreWebView2Environment.CreateAsync(Nothing, userDataFolder)
Catch ex As Exception
‘ Runtimeが見つからない、または初期化失敗
MessageBox.Show($”WebView2 Runtimeの初期化に失敗しました。{vbCrLf}” &
$”最新のMicrosoft Edge WebView2 Runtimeがインストールされていることを確認してください。{vbCrLf}” &
$”エラー詳細: {ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
Return
End Try
‘ WebView2コントロールのCoreWebView2を初期化
Await WebView21.EnsureCoreWebView2Async(env)
‘ Webページが完全にロードされた後に実行されるイベントハンドラを設定
AddHandler WebView21.CoreWebView2.NavigationCompleted, AddressOf CoreWebView2_NavigationCompleted
‘ JavaScriptからのメッセージを受信するイベントハンドラを設定
AddHandler WebView21.CoreWebView2.WebMessageReceived, AddressOf CoreWebView2_WebMessageReceived
‘ ローカルHTMLファイルをロードするための仮想ホスト設定
Dim localResourceFolder As String = Path.Combine(Application.StartupPath, “WebResources”)
If Not Directory.Exists(localResourceFolder) Then
MessageBox.Show($”Webリソースフォルダ ‘{localResourceFolder}’ が見つかりません。”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
Return
End If
‘ 仮想ホストを登録。Webリソースフォルダを ‘myapp.local’ ドメインとしてマッピング
WebView21.CoreWebView2.SetVirtualHostNameToFolderMapping( _
“myapp.local”, _
localResourceFolder, _
CoreWebView2HostResourceAccessKind.Allow)
‘ 仮想ホスト経由でローカルのHTMLファイルをロード
WebView21.CoreWebView2.Navigate(“https://myapp.local/index.html”)
Catch ex As Exception
MessageBox.Show($”WebView2の初期化中に予期せぬエラーが発生しました: {ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
End Try
End Function
‘ Webページのナビゲーション完了イベントハンドラ
Private Async Sub CoreWebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
If e.IsSuccess Then
Debug.WriteLine($”ナビゲーション完了: {e.Uri}”)
‘ JavaScriptに初期化メッセージを送信する例
Dim initMessage As New With {
.action = “initialize”,
.data = “WinFormsアプリからの初期データ”
}
Dim jsonString As String = JsonConvert.SerializeObject(initMessage)
WebView21.CoreWebView2.PostWebMessageAsJson(jsonString)
Debug.WriteLine($”JSへ初期メッセージを送信: {jsonString}”)
Else
MessageBox.Show($”ナビゲーションエラー: {e.WebErrorStatus}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
‘ JavaScriptからのメッセージ受信イベントハンドラ
Private Async Sub CoreWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
Dim messageJson As String = e.WebMessageAsJson
Debug.WriteLine($”JSからメッセージ受信: {messageJson}”)
Try
‘ 受信したJSONを動的オブジェクトとしてデシリアライズ
Dim receivedData As JObject = JObject.Parse(messageJson)
Dim action As String = receivedData(“action”)?.ToString()
Select Case action
Case “logMessage”
Dim jsMessage As String = receivedData(“data”)?.ToString()
MessageBox.Show($”JSからログメッセージ: {jsMessage}”, “JSメッセージ”, MessageBoxButtons.OK, MessageBoxIcon.Information)
Case “requestData”
‘ JavaScriptからのデータ要求に応答する例
Dim requestedType As String = receivedData(“dataType”)?.ToString()
Dim responseData As New With {
.action = “receiveData”,
.dataType = requestedType,
.payload = $”VB.NETから提供された {requestedType} データ ({DateTime.Now.ToLongTimeString()})”
}
Dim responseJson As String = JsonConvert.SerializeObject(responseData)
WebView21.CoreWebView2.PostWebMessageAsJson(responseJson)
Debug.WriteLine($”JSへデータ応答を送信: {responseJson}”)
Case “submitForm”
Dim formData As JObject = CType(receivedData(“formData”), JObject)
MessageBox.Show($”JSからフォームデータを受信しました:{vbCrLf}{formData.ToString(Formatting.Indented)}”, “フォームデータ”, MessageBoxButtons.OK, MessageBoxIcon.Information)
‘ ここでデータベース保存などのビジネスロジックを実行
Await SaveFormDataToDatabase(formData)
‘ 処理結果をJavaScriptに返す
Dim resultMessage As New With {
.action = “formSubmitResult”,
.success = True,
.message = “データは正常に処理されました。”
}
WebView21.CoreWebView2.PostWebMessageAsJson(JsonConvert.SerializeObject(resultMessage))
Case Else
MessageBox.Show($”未知のアクション: {action}{vbCrLf}メッセージ: {messageJson}”, “JSメッセージエラー”, MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Select
Catch ex As Exception
MessageBox.Show($”JavaScriptからのメッセージ解析中にエラー: {ex.Message}{vbCrLf}メッセージ: {messageJson}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
‘ VB.NETからJavaScript関数を呼び出すボタン
Private Async Sub ButtonCallJsFunction_Click(sender As Object, e As EventArgs) Handles ButtonCallJsFunction.Click
If WebView21.CoreWebView2 IsNot Nothing Then
Try
Dim jsCode As String = “updateMessage(‘VB.NETから直接呼び出されたメッセージ’);”
Dim result As String = await WebView21.CoreWebView2.ExecuteScriptAsync(jsCode)
Debug.WriteLine($”JS関数呼び出し結果: {result}”)
Catch ex As Exception
MessageBox.Show($”JavaScript関数呼び出しエラー: {ex.Message}”, “エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End If
End Sub
‘ VB.NETからJavaScriptへデータを送信するボタン
Private Async Sub ButtonSendToJs_Click(sender As Object, e As EventArgs) Handles ButtonSendToJs.Click
If WebView21.CoreWebView2 IsNot Nothing Then
Dim currentTimestamp As String = DateTime.Now.ToString(“yyyy-MM-dd HH:mm:ss”)
Dim dataToSend As New With {
.action = “updateUI”,
.value = $”VB.NETから送信された最新情報 @ {currentTimestamp}”
}
Dim jsonString As String = JsonConvert.SerializeObject(dataToSend)
WebView21.CoreWebView2.PostWebMessageAsJson(jsonString)
Debug.WriteLine($”JSへデータ送信: {jsonString}”)
End If
End Sub
‘ フォームクローズ時のリソース解放
Private Sub MainForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
If WebView21 IsNot Nothing AndAlso WebView21.CoreWebView2 IsNot Nothing Then
‘ イベントハンドラを解除してメモリリークを防ぐ
RemoveHandler WebView21.CoreWebView2.NavigationCompleted, AddressOf CoreWebView2_NavigationCompleted
RemoveHandler WebView21.CoreWebView2.WebMessageReceived, AddressOf CoreWebView2_WebMessageReceived
WebView21.Dispose() ‘ WebView2コントロールのリソースを解放
End If
End Sub
‘ ダミーのデータベース保存処理
Private Async Function SaveFormDataToDatabase(formData As JObject) As Task
‘ ここに実際のデータベース保存ロジックを実装
‘ 非同期処理の例として、少し待機
Await Task.Delay(1000)
Debug.WriteLine($”データベース保存シミュレーション: {formData.ToString()}”)
‘ 例: DB接続、SQL実行など…
End Function
End Class
WebResources/index.html (HTML & JavaScript) コード
アプリケーションの実行ファイルと同じディレクトリに `WebResources` フォルダを作成し、その中に `index.html` を配置してください。
WebView2 JavaScript <–> VB.NET 通信
- WebView2とは何か:Chromiumの鼓動がWinFormsに宿る
- 移行への第一歩:準備とセットアップの極意
- 1. 前提条件の確認とNuGetパッケージのインストール
- 2. WebView2ランタイムの扱い:配布戦略の肝
- WebBrowserからWebView2へのコード移行:断絶と再構築
- 1. 基本的なコントロール操作の変革
- 2. イベントモデルの進化:非同期処理の徹底
- 3. JavaScriptとの連携:双方向通信の真髄
- a. VB.NETからJavaScriptを呼び出す (`ExecuteScriptAsync`)
- b. VB.NETからJavaScriptへデータを送る (`PostWebMessageAsString`/`PostWebMessageAsJson`)
- c. JavaScriptからVB.NETへデータを送る (`WebMessageReceived`イベント)
- d. 堅牢な通信設計の原則
- 実践的設計パターン:業務効率化ツールを支える堅牢なアーキテクチャ
- 1. ファイル連携の注意点:セキュリティとアクセス制御
- 2. データベース連携の注意点:APIサーバー経由の鉄則
- 3. 長期運用を見据えた設計:リソース、ログ、バージョンアップ
- コピペで動くプロダクションコード例:実践の羅針盤
- WinForms (VB.NET) コード
- WebResources/index.html (HTML & JavaScript) コード
- JavaScriptからVB.NETへ
- VB.NETからJavaScriptへ
JavaScriptからVB.NETへ
VB.NETからJavaScriptへ
このコードでは、`window.chrome.webview.postMessage` と `window.chrome.webview.addEventListener(‘message’, …)` を介して、VB.NETとJavaScriptがシームレスに連携する。`action`プロパティを使って、メッセージの種類を識別し、それぞれの処理をディスパッチする設計は、堅牢な通信プロトコルを構築する上で極めて重要だ。
よくある落とし穴と回避策:経験者の知恵
1. `CoreWebView2`プロパティが`Nothing`になる問題
- 落とし穴: `WebView21.CoreWebView2.Navigate(…)`などを呼び出す前に、`CoreWebView2`が初期化されていないと`NullReferenceException`が発生する。`Form_Load`の段階ではまだ初期化が完了していないことが多い。
- 回避策: 必ず`Await WebView21.EnsureCoreWebView2Async()`を呼び出し、初期化が完了するのを待機してから`CoreWebView2`プロパティにアクセスせよ。
2. UIスレッドのブロック問題
- 落とし穴: `ExecuteScriptAsync`などの非同期メソッドの結果を待つ際に、`Result`プロパティに直接アクセスしたり、`Wait()`を呼び出したりすると、UIスレッドがブロックされ、アプリケーションがフリーズする。
- 回避策: `Async/Await`パターンを徹底せよ。`ExecuteScriptAsync`の結果を待つ際は、必ず`Await`を使用し、UIスレッドを解放すること。イベントハンドラやUI操作に関連するメソッドは`Async Sub`または`Async Function`として定義する。
3. JavaScriptの実行結果が期待通りでない
- 落とし穴: `ExecuteScriptAsync`が返す結果が空文字列だったり、予期しない値だったりする。
- 回避策:
- デバッグツールの活用: WebView2内では、Microsoft EdgeのF12開発者ツールが利用できる。`WebView21.CoreWebView2.OpenDevToolsWindow()`を呼び出してデバッガーを起動し、JavaScriptのエラーやコンソール出力を確認せよ。
- 戻り値の確認: JavaScript側で`return`ステートメントが正しく記述されているか確認する。特にPromiseを返すJavaScript関数の場合、Promiseの解決を待つ必要がある。
4. セキュリティコンテキストの理解
- 落とし穴: WebView2内のWebコンテンツを、通常のWebブラウザと同じセキュリティレベルで扱ってしまう。
- 回避策: ローカルファイルシステムへのアクセスや、WinFormsアプリへのメッセージ送信は、悪意のあるWebコンテンツによって悪用される可能性がある。`CoreWebView2_PermissionRequested`イベントで権限要求を適切に処理したり、`CoreWebView2_NewWindowRequested`でポップアップを制御するなど、セキュリティを意識した設計を心がけるべきだ。仮想ホストマッピング (`SetVirtualHostNameToFolderMapping`) を使うことで、セキュリティを向上させる。
未来への展望:WebView2が拓くWinFormsの可能性
WebView2への移行は、単なる技術的アップデートではない。それは、古き良きWinFormsアプリケーションが、現代のWeb技術と融合し、新たな可能性を切り開くための戦略的な一歩だ。
- PWA (Progressive Web App) の統合: WebView2は、PWAをWinFormsアプリ内にシームレスに統合するための強力な基盤となる。Webの利便性とデスクトップの堅牢性を兼ね備えたハイブリッドアプリの構築が可能になる。
- UX (ユーザー体験) の飛躍的向上: 最新のWebUI/UXトレンドを取り入れることで、業務ツールのユーザー体験は劇的に向上する。CSSアニメーション、レスポンシブデザイン、リッチなインタラクティブ要素など、Webの表現力を最大限に活用せよ。
- 開発効率の向上: Webフロントエンドの豊富なライブラリやフレームワーク、開発ツール群をWinFormsアプリ内で活用できるため、開発効率は飛躍的に向上する。
諸君、WebView2は、WinFormsアプリケーションを未来へと導くための強力な武器だ。この「極限の知見」を胸に、堅牢で、保守性が高く、そして何よりもユーザーに喜ばれるモダンな業務効率化ツールを構築してほしい。レガシーの呪縛を完全に解き放ち、WinFormsの新たな時代を築くのは、他ならぬ諸君の使命である。
