レガシーな MFC アプリの中に、CHtmlView を使った画面が残っていることがあります。以前は社内のヘルプ画面や簡単な HTML 表示で使われていましたが、最近のログイン画面や JavaScript を使ったページでは正しく動かないことがあります。
この記事では、MFC アプリに WebView2 を組み込むときに確認する SDK、Runtime、Loader DLL と、MFC 側で必要になる親ウィンドウ、表示範囲、リサイズ処理を解説します。
まず確認するもの
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;
ただし、WebView2.h が見えるだけでは実行確認として不十分です。実行時には WebView2Loader.dll と WebView2 Runtime も必要です。NuGet 復元後に出力フォルダーへ Loader DLL がコピーされているかも確認します。
手順 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());
ExecuteScript の戻り値は、そのまま文字列ではなく JSON エンコードされた結果として返る点に注意してください。
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 追加後も、実行時には
WebView2Loader.dllと WebView2 Runtime が必要 CHtmlViewからの移行では、DOM 操作や JavaScript 連携の設計も見直す
