Webview2 之于 windows,等同于 webview 之于安卓系统。Chromium 真是遍地开花啊。目前,Webview2处于公测阶段,用户需安装 edge 的内部版本(insider)才能使用;外界开发者则面临着暂时的文档不够详细、demo无法运行的问题。
官方 demo 仓库:https://github.com/MicrosoftEdge/WebView2Samples
或者(比demo高级点):https://github.com/MicrosoftEdge/WebView2Browser
其中 Webview2Gettingstarted 是最简单的win32示例项目。成功编译运行后,打开一个空窗口,里面一个Webview2控件,导航至www.bing.com。Webview2 功能齐全,右键菜单、页内搜索、下载、常用快捷键(刷新/放大/打印)、dev控制台等等样样具备,关键是产品体积小,运行内存也不大,实在是太振奋人心了!
不过代码不全,需配合官方教程,该复制的复制,头文件该补的补。
源文件仅一个HelloWebview.cpp:https://github.com/MicrosoftEdge/WebView2Samples/blob/master/GettingStartedGuides/Win32_GettingStarted/HelloWebView.cpp
Demo的核心代码如下:
//这个库需要自己创建窗口 hWnd
// <-- WebView2 sample code starts here -->
// Step 3 - Create a single WebView within the parent window
// Locate the browser and set up the environment for WebView
auto options = Microsoft::WRL::Make();
//CreateCoreWebView2EnvironmentWithOptions(TEXT("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\85.0.564.63")
// , TEXT("C:\\Users\\TEST\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default")
// , options.Get(),
static wil::com_ptr m_webView;
static wil::com_ptr m_webViewEnvironment;
CreateCoreWebView2EnvironmentWithOptions(0,0,0,
Callback(
[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {
m_webViewEnvironment=env;
// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
env->CreateCoreWebView2Controller(hWnd, Callback(
[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
if (controller != nullptr) {
webviewController = controller;
webviewController->get_CoreWebView2(&m_webView);
}
// Add a few settings for the webview
// The demo step is redundant since the values are the default settings
ICoreWebView2Settings* Settings;
m_webView->get_Settings(&Settings);
Settings->put_IsScriptEnabled(TRUE);
Settings->put_AreDefaultScriptDialogsEnabled(TRUE);
Settings->put_IsWebMessageEnabled(TRUE);
// Resize WebView to fit the bounds of the parent window
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->put_Bounds(bounds);
// Schedule an async task to navigate to Bing
m_webView->Navigate(L"https://www.bing.com/");
// 最简单的demo建立完毕。
// 以下自行测试各种功能……
// Step 4 - Navigation events
// Step 5 - Scripting
// Step 6 - Communication between host and web content
return S_OK;
}).Get());
return S_OK;
}).Get());
// <-- WebView2 sample code ends here -->
另有一个示例项目Webview2ApiSample,代码更全,但是无法编译,打开了当参考也好,比看文档强。
成功运行最简Demo后,就可以测试各种功能了:
- 加载字符串: m_webView->NavigateToString((LPCWSTR)L"哈哈哈哈哈");
管中窥豹,Webview2 的API命名是与众不同的,别人load、load,他偏偏要 Navigate……
- 添加资源拦截器,拦截所有图片
static EventRegistrationToken m_webResourceRequestedTokenForImageBlocking = {};
//add Filter
m_webView->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE);
//add Handler
CHECK_FAILURE(m_webView->add_WebResourceRequested(
Callback(
[hWnd](
ICoreWebView2* sender,
ICoreWebView2WebResourceRequestedEventArgs* args) {
COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext;
CHECK_FAILURE(
args->get_ResourceContext(&resourceContext));
// Ensure that the type is image
if (resourceContext != COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE)
{
return E_INVALIDARG;
}
// Override the response with an empty one to block the image.
// If put_Response is not called, the request will continue as normal.
wil::com_ptr response;
//wil::com_ptr> response;
m_webViewEnvironment->CreateWebResourceResponse(nullptr, 403 /*NoContent*/, L"Blocked", L"", &response);
CHECK_FAILURE(args->put_Response(response.get()));
return S_OK;
}).Get(), &m_webResourceRequestedTokenForImageBlocking));
CHECK_FAILURE 来自于 Webview2ApiSample,没什么用,嫌麻烦就拿掉或者
#define CHECK_FAILURE
。这个lambda表达式是个啥结构暂时看不懂,照样复制粘贴呗!
- 拦截所有图片,然后替换为本地文件:
wil::com_ptr stream;
CHECK_FAILURE(SHCreateStreamOnFileEx(
L"D:\\1.jpg", STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr,
&stream));
m_webViewEnvironment->CreateWebResourceResponse(stream.get(), 200, L"OK", L"", &response);
- 拦截网址访问,返回中文字符串
if (resourceContext == COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT)
{
wil::com_ptr req;
args->get_Request(&req);
wil::unique_cotaskmem_string navigationTargetUri;
req->get_Uri(&navigationTargetUri);
std::wstring uriTarget(navigationTargetUri.get());
if(uriTarget==L"http://test/MDT/1.html");
{
CHAR test_buffer1[1024];
sprintf(test_buffer1, "%s", " 1234哈哈哈");
wil::com_ptr response;
wil::com_ptr stream = SHCreateMemStream((const BYTE*)test_buffer1, strlen(test_buffer1));
CHECK_FAILURE(m_webViewEnvironment->CreateWebResourceResponse(stream.get(), 200, L"OK", L"charset=utf-8", &response));
wil::com_ptr headers;
CHECK_FAILURE(response->get_Headers(&headers));
headers->AppendHeader(L"Content-Type", L"text/html; charset=utf-8");
CHECK_FAILURE(args->put_Response(response.get()));
return S_OK;
}
}
注意在响应头
Content-Type
中定义正确的MIME类型和编码字符集,否则中文乱码,不能正常显示。
JS调用C++
这个教程第六步有介绍,有点不同,是通过 postMessage 实现的。C++调用JS
m_webView->ExecuteScript(L"window.document.URL;", Callback(
[](HRESULT errorCode, LPCWSTR resultObjectAsJson) -> HRESULT {
LPCWSTR URL = resultObjectAsJson;
//doSomethingWithURL(URL);
return S_OK;
}).Get());
stdfx.h
#include
#include
#include
#include
#include
#include
// include WebView2 header
#include "WebView2.h"
#include "WebView2EnvironmentOptions.h"
// include WebView2 header
using namespace Microsoft::WRL;
编译后的产品有一个 100kb 的依赖: WebView2Loader.dll
还行,很不错!
“将Libcef打造为win32控件”系列:
一:初识Libcef
二:初次封装,拿到浏览器HWND
三:资源拦截替换、JS调用C++ Native、首次运用