文档层级
- Docs
- Microsoft Edge documentation
- Microsoft Edge Developer documentation
- WebView2
- Fundamentals
- Web and native interop
- Fundamentals
- WebView2
- Microsoft Edge Developer documentation
- Microsoft Edge documentation
Interop of native-side and web-side code
介绍了一些用例
- 当前进到不同的网页时,更新宿主窗口
- 发送一个原生的camera object给web app使用
- 为当前的应用在web侧执行一个特定的js文件
Before you begin
https://github.com/MicrosoftEdge/WebView2Samples存储了一些示例。
Scenario: Simple messaging
Send Messages from the host app to WebView2
通过PostWebMessageAsJson
发送Json文本给WebView2.
WebView2中通过侦听message
事件来获取Json文本。
也可以通过PostWebMessageAsString
发送字符串。
Receive message strings via postMessage
WebView2通过window.chrome.webview.postMessage
向宿主发送消息。
宿主需要通过注册WebMessageReceived
来接受消息。
消息内容为纯文本,格式需要应用自己定义。
Round-trip messages
略。
Scenario: Send JavaScript code
宿主通过`ExecuteScriptAsync异步调用某JS代码。 具体查看Use JavaScript in WebView2 (Run JavaScript from native code)。
Scenario: Send native objects
将一个原生对象传给WebView2,让其能够调用原生对象的方法。
这种情况下,消息被封装成了对象方法的调用。
具体的设置是通过AddHostObjectToScript
达成的。
在WebView2中使用window.chrome.webview.hostObjects.{name}
来访问这些原生对象。
Call web-side code from native-side code
Basic WebView2 functions
除了ExecuteScriptAsync,还有OnDocumentCreatedAsync。后者用于注册在DOM加载完成时执行的代码。
Scenario: ExecuteScript JSON-encoded results
略。
Scenario: Running a dedicated script file
略。
Scenario: Removing drag-and-drop functionality
略。
Scenario: Removing the context menu
略。
Call native-side code from web-side code
介绍AddHostObjectToScript用编口(API)。
目的是让运行于沙箱中的web代码能够原生对象的功能。比如调用网摄头(webcam)。
本文中使用的例子是WebView2 Win32 sample app。更多参考Embed web content into native applications。
Step 1: Install Visual Studio, install git, clone the WebView2Samples repo, and open the solution
过。
Step 2: Define the host object and implement IDispatch
Part 2A: Create the COM interface
此处使用HostObjectSample.idl
定义COM类的接口。
该COM类,也就是HostObjectSample,需要实现两个接口, 一个是IDispatch,用于AddHostObjectToScript; 另一个是自定义的,用于WebView2交互的接口。
IDispatch意味着调用是JS中动态创建的,更多参考IDispatch interface (oaidl.h)。
实现IDispatch的话则参考Type Libraries and the Object Description Language。
编译IDL文件会产生translation lookaside buffer (TLB)文件。需要把这个TLB文件加入到项目中。
实际的代码在
SampleApps/WebView2APISample/HostObjectSample.idl
Part 2B: Create the C++ object
创建COM使用的是Windows Runtime C++ Template Library (WRL),已经被C++/WinRT替代了。
实际的代码在
- SampleApps/WebView2APISample/HostObjectSampleImpl.cpp
- SampleApps/WebView2APISample/HostObjectSampleImpl.h
Step 3: Call the AddHostObjectToScript API
m_hostObject.query_to<IDispatch>(&remoteObjectAsVariant.pdispVal);
将COM例现转成IDispatch接口的例现,并盛放在VARIANT类型的remoteObjectAsVariant标的的属域pdispVal中。
需要显示设置VARIANT所含的数据类型:remoteObjectAsVariant.vt = VT_DISPATCH
。
关于VARIANT是一种动态数据类型,其支持的数据类型可以在https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant查的。AddHostObjectToScript只支持VARIANT中的部分数据类型,查看WebView2 Win32 C++ ICoreWebView2 | Microsoft Docs 。
Step 4: Use AddHostObjectToScript to pass a method to the web
在ScenarioAddHostObject.html
中可以看到前面注册的宿主对象的使用。
同步调用和异步调用的方式不太一样:
document.getElementById("invokeMethodAsyncButton").addEventListener("click", async () => {
const paramValue1 = document.getElementById("invokeMethodAsyncParam1").value;
const paramValue2 = parseInt(document.getElementById("invokeMethodAsyncParam2").value);
const resultValue = await chrome.webview.hostObjects.sample.MethodWithParametersAndReturnValue(paramValue1, paramValue2);
document.getElementById("invokeMethodAsyncOutput").textContent = resultValue;
});
document.getElementById("invokeMethodSyncButton").addEventListener("click", () => {
const paramValue1 = document.getElementById("invokeMethodSyncParam1").value;
const paramValue2 = parseInt(document.getElementById("invokeMethodSyncParam2").value);
const resultValue = chrome.webview.hostObjects.sync.sample.MethodWithParametersAndReturnValue(paramValue1, paramValue2);
document.getElementById("invokeMethodSyncOutput").textContent = resultValue;
});
另外chrome.webview.hostObjects.options.shouldSerializeDates = true;
是啥意思?
其他
- 在WinRT中使用interface ICoreWebView2Interop。
- https://stackoverflow.com/questions/69441352/webview2-addhostobjecttoscript-cant-access-function-with-parameters
(完)
2022-03-27更新
Win32 C++ WebView2 API conventions
Async methods
异步方法使用待理工(delegate)接口来联系告知以下信息:
- 异步执行的结束
- 成功还是失败
- 执行获得的结果
待理工接口有一个Invoke方法,首参是HRESULT,用作成败码;可选的二参用作执行结果。
例如,ICoreWebView2::CapturePreview接受一个ICoreWebView2CapturePreviewCompletedHandler指针,作为最终的参数。对于ICoreWebView2CapturePreviewCompletedHandler,可以手动实现,也可以使用WRL Callback实现。
下面是针对ICoreWebView2::ExecuteScript的示例:
void ScriptComponent::InjectScript()
{
TextInputDialog dialog(
m_appWindow->GetMainWindow(),
L"Inject Script",
L"Enter script code:",
L"Enter the JavaScript code to run in the webview.",
L"window.getComputedStyle(document.body).backgroundColor");
if (dialog.confirmed)
{
m_webView->ExecuteScript(dialog.input.c_str(),
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[](HRESULT error, PCWSTR result) -> HRESULT
{
if (error != S_OK) {
ShowFailure(error, L"ExecuteScript failed");
}
MessageBox(nullptr, result, L"ExecuteScript Result", MB_OK);
return S_OK;
}).Get());
}
}
Events
时间通过add_EventName、remove_EventName来添加和移除。添加的时候,需要传入一个代理工,返回的是一个EventRegistrationToken号牌。此号牌可用于在remove_EventName中移除事件处理。
事件代理工接口也只有单个Invoke方法,首参为事件发起者,二参为事件参量。
下面是针对NavigationCompleted的示例;
// Register a handler for the NavigationCompleted event.
// Check whether the navigation succeeded, and if not, do something.
// Also update the Cancel buttons.
CHECK_FAILURE(m_webView->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args)
-> HRESULT {
BOOL success;
CHECK_FAILURE(args->get_IsSuccess(&success));
if (!success)
{
COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus;
CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus));
if (webErrorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED)
{
// Do something here if you want to handle a specific error case.
// In most cases it is not necessary, because the WebView
// displays an error page automatically.
}
}
m_toolbar->SetItemEnabled(Toolbar::Item_CancelButton, false);
m_toolbar->SetItemEnabled(Toolbar::Item_ReloadButton, true);
return S_OK;
})
.Get(),
&m_navigationCompletedToken));
Strings
字符串输出参数是LPWSTR的以null结尾的,并且是通过CoTaskMemAlloc分配的。所有权会被传递给请求方,用完需要使用CoTaskMemFree来释放。
字符串输入参数也是LPWSTR的以null结尾的。对于同步的函数请求,请求方保证在调用期间字符串不会失效。如果接收方需要在函数请求结束时必须使用,必须存一个副本。
URI and JSON parsing
许多方法的参数类型时URI和JSON字符串。
你可以使用所偏好的料库来处理URI和JSON。
比如WinRT的
- RuntimeClass_Windows_Data_Json_JsonObject、IJsonObjectStatics
- RuntimeClass_Windows_Foundation_Uri、IUriRuntimeClassFactory
如果使用IUri以及CreateUri,你可能要使用下列的URI创建旗标,以匹配WebView的URI解析:
Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME | Uri_CREATE_NO_DECODE_EXTRA_INFO
WebView2 Win32 AddHostObjectToScript
宿主标的通过代理window.chrome.webview.hostObjects.{name}
体现出来。宿主标的是promise,且可以化解成一个表征宿主标的代理。此promise会被拒绝,如果应用没有添加{name}
的宿主标的。代理上对宿主标的方法的访问也是通过promise进行的。
支持的是简单类型、IDispatch和数组。实现了IDispatch的IUnknown也被支持。一般化的IUnkown、VT_DECIAL或VT_RECORD不受支持。远端的JS标的,比如说回调函数,被表示为VT_DISPATCH类别的VARIANT。JS回调方法可以使用DISPID的DISPID_VALUE来调用。嵌套的数组最多支持三层嵌套。不支持引用类型的数组。VT_EMPTY和VT_NULL映射成JS的null。JS中的null和undefined则映射成VT_EMPTY。
宿主标的也呈现在window.chrome.webview.hostObjects.sync.{name}
。此处的代理执行的时候采用的是同步的方式,会阻塞正在运行的脚本。JS阻塞的时候,无法在宿主代码中执行JS回调,并且会返回失败码HRESULT_FROM_WIN32(ERROR_POSSIBLE_DEADLOCK)
。
异步代理和同步代理可以指向同一个宿主标的。在一个代理上做的改动,在另一个代理上可见。
宿主标的代理,本质上是JS代理标的,可以拦截对所有辖属的访问、方法的调用。作为Function或者标的原型一部分的辖属或方法是本地(JS环境)运行的。任何在chrome.webview.hostObjects.options.forceLocalProperties
也是本地运行的,默认包含toJSON
以及Symbol.toPrimitive
。
chrome.webview.hostObjects.cleanupSome
可以为宿主标的代理执行尽力而为的垃圾回收。
chrome.webview.hostObjects.options
提供了一些调整宿主标的的能力:
- forceLocalProperties
- log,默认为null,一个使用示例是
console.log.bind(console)
,暂时不明所以 - shouldSerializeDates,
- 为false的时候JS的Date标的会使用JSON。stringify之后以字符串形式发送给宿主
- 为true的时候可以序列化成VT_DATE发给宿主
下列方法在代理上也是本地化运行的:
- applyHostFunction、getHostProperty、setHostProperty
- getLocalProperty、setLocalProperty
- sync,示例
const syncProxy = await chrome.webview.hostObjects.sample.methodCall().sync()
- async,示例
const asyncProxy = chrome.webview.hostObjects.sync.sample.methodCall().async()
- then
在异步宿主标的代理上设置一个辖属会立马返回。这是JS代理标的的要求。如果需要异步等待辖属设置完成,可以使用setHostProperty方法。
参考
(更新完)