MFCのダイアログやウィンドウにWebView2を埋め込むと、Edge (Chromium) のエンジンでWebページを表示できます。古いMFCアプリに残っているCHtmlViewの画面はIEベースなので、最近のログイン画面やJavaScriptを使ったページが正しく動かないことがあり、その置き換えにも使えます。
MFCでWebView2を埋め込み、表示するまでの手順は、次の4つです。
- NuGetでMicrosoft.Web.WebView2を追加する。WebView2はCOMのAPIなので、InitInstanceでAfxOleInitを呼んでCOMを初期化しておく
- 親ウィンドウの中で、WebView2を表示する範囲をRECTで決める
- 親ウィンドウのHWNDができてから(ダイアログならOnInitDialog、フレームやビューならOnCreate)、CreateCoreWebView2EnvironmentWithOptionsで環境を作り、CreateCoreWebView2Controllerでコントローラーを作り、put_Boundsで範囲を渡してから表示する。環境とコントローラーの作成は、完了ハンドラーで結果を受け取る非同期の呼び出しになる
- 親ウィンドウのサイズが変わったら、OnSizeでput_Boundsを呼び直す
WebView2.hが見つかってビルドが通っても、実行するPCにWebView2 Runtimeが無いと表示できません。LoaderをDLLとして使う構成(NuGetの既定)では、WebView2Loader.dllも実行ファイルと同じフォルダーに要ります。そのため、手順のコードの前に、ビルド時に使うものと実行時に使うものの違いから書きます。
まず確認するもの
WebView2では、ビルド時に必要なものと実行時に必要なものが分かれています。WebView2.hが見つかるだけでは、まだ表示できるとは限りません。
| 確認するもの | 役割 |
|---|---|
| WebView2 SDK | WebView2.hやCOMインターフェイスを使うための開発用パッケージ |
| WebView2 Runtime | Edge Chromiumベースで実際に表示するための実行環境 |
| WebView2Loader.dll | アプリからWebView2 Runtimeを読み込むためのDLL |

最初にSDKを追加し、次にRuntimeとLoader DLLを確認します。そのうえで、MFC側ではどのHWNDにWebView2をぶら下げるか、どの範囲に表示するかを決めます。
WebView2とは
WebView2は、Microsoft Edge Chromiumのレンダリングエンジンをデスクトップアプリケーションに埋め込むためのコントロールです。MFCから見ると、「MFCのウィンドウ内に、Edgeベースのブラウザー領域を子要素として作る」イメージです。
CHtmlViewはIEベースのため、現代的なJavaScript、CSS、認証画面、管理画面UIなどで問題が出やすくなります。WebView2へ移行すると、Edge Chromiumベースの表示に置き換えられます。
手順1: NuGetパッケージを追加する
Visual StudioのNuGetパッケージマネージャーで、対象プロジェクトに次のパッケージを追加します。
- Microsoft.Web.WebView2
追加後、C++ 側で次のヘッダーを参照できるようになります。
#include <WebView2.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
using Microsoft::WRL::Callback;
ただし、WebView2.hが見えるだけでは実行確認として不十分です。実行時にはWebView2 Runtimeが必要です。LoaderをDLLとして使う構成(NuGetの既定)ではWebView2Loader.dllも要るので、NuGet復元後に出力フォルダーへコピーされているかも確認します。
手順2: WebView2を表示する矩形を決める
WebView2は、MFCのビューやダイアログに自動で配置されるわけではありません。親ウィンドウの中で、どの範囲をブラウザー領域にするかをRECTとして決めます。
CRect hostRect;
GetClientRect(&hostRect);
hostRect.DeflateRect(12, 96, 12, 64);
この例では、上部に説明領域、中央にWebView2のhost rectangle、下部にステータス欄を置いています。実際のHTML表示は、この中央の矩形に対して行います。
手順3: 非同期初期化の順番を崩さない
WebView2の初期化は非同期で進みます。環境を作り、Controllerを作り、ICoreWebView2を取得し、最後にNavigateまたはNavigateToStringで表示します。
// メンバ変数
ComPtr<ICoreWebView2Controller> m_controller;
ComPtr<ICoreWebView2> m_webView;
// 親 HWND が有効になった後で呼ぶ
void CMainFrame::StartWebView()
{
CreateCoreWebView2EnvironmentWithOptions(
nullptr,
nullptr,
nullptr,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
{
if (FAILED(result) || env == nullptr)
{
return S_OK;
}
env->CreateCoreWebView2Controller(
m_hWnd,
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[this](HRESULT controllerResult, ICoreWebView2Controller* controller) -> HRESULT
{
if (FAILED(controllerResult) || controller == nullptr)
{
return S_OK;
}
m_controller = controller;
m_controller->get_CoreWebView2(&m_webView);
RECT bounds = HostBounds();
m_controller->put_Bounds(bounds);
m_webView->NavigateToString(L"<html><body>WebView2 OK</body></html>");
return S_OK;
}).Get());
return S_OK;
}).Get());
}
よくある失敗は、親ウィンドウのHWNDがまだ有効でないタイミングで初期化することです。MFCでは、ビューならOnCreate以降、ダイアログならOnInitDialog以降に寄せると整理しやすくなります。
手順4: リサイズ時にBoundsを更新する
WebView2のControllerは、MFCのレイアウト変更に自動追従しません。親ウィンドウのサイズが変わったら、put_Boundsを呼び直します。
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
__super::OnSize(nType, cx, cy);
LayoutClient(CRect(0, 0, cx, cy));
if (m_controller)
{
RECT bounds = HostBounds();
m_controller->put_Bounds(bounds);
}
}
この更新を入れておくと、ウィンドウをリサイズしたときにWebView2の表示領域もMFC側のレイアウトに追従します。
C++ とJavaScriptの連携
WebView2では、C++ 側からJavaScriptを実行できます。たとえば、表示中ページのタイトルを取得する場合はExecuteScriptを使います。
m_webView->ExecuteScript(
L"document.title",
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[](HRESULT hr, LPCWSTR result) -> HRESULT
{
// result には JSON エンコードされた戻り値が入る
TRACE(_T("Title: %s\n"), result);
return S_OK;
}).Get());
JavaScriptの評価結果は、ExecuteScriptの戻り値ではなく、完了ハンドラーのresultにJSONエンコードされた文字列として渡されます。たとえばdocument.titleの結果は、前後に二重引用符が付いた文字列になります。
CHtmlViewからの移行で見る対応関係
| CHtmlView / IE | WebView2 / Edge Chromium |
|---|---|
| Navigate2 | ICoreWebView2::Navigate |
| GetHtmlDocument | ExecuteScriptやWeb側のDOM API |
| ActiveX / IEコンポーネント | WebView2 COM API |
| IEレンダリング | Edge Chromiumレンダリング |
移行時は、単純なメソッド名の置き換えだけでは済まないことがあります。特に、DOMを直接触っていた処理はJavaScript実行やWeb側のイベント設計へ寄せる必要があります。
まとめ
- SDKが入ったら、WebView2.hの検出で止めず、実際のWebView2表示まで確認する
- MFC側では、親HWND、host rectangle、非同期初期化、OnSizeのBounds更新が重要
- NuGet追加後も、実行時にはWebView2 Runtimeが必要。LoaderをDLLとして使う構成ではWebView2Loader.dllも必要
- CHtmlViewからの移行では、DOM操作やJavaScript連携の設計も見直す
