cocos webview js和c++交互

简介: cocos webview js和c++交互

cocos自带了webview组件,对于使用者来说,lua层接口非常简单:

local webView = ccexp.WebView:create()
webView:loadURL(url)
复制代码

2dx仓库中关于webview-win32的pr,但是好像并未完善webview js和c++的交互功能,仅仅实现了一个简单的webview展示。

关于win32 web browser的资料网上的晦涩难懂,找到的一个WebBrowser2-Demo

C++接口的实现代码review

class IDispatch{
  virtual  HRESULT invoke(...);// 
}
class DWebBrowserEvents2:public IDispatch{
}
class Win32WebControl:public DWebBrowserEvents2{
}
复制代码
  • createWebView的实现
CAxWindow _winContainer; // 窗口对象的句柄
IWebBrowser2 *_webBrowser2;// webview控件
Win32WebControl::createWebView(){
  HWND hwnd = cocos2d::Director::getInstance()->getOpenGLView()->getWin32Window();
  _winContainer.Create(hwnd, NULL, NULL, WS_CHILD | WS_VISIBLE);
  // 创建 ActiveX 控件,初始化它并在指定窗口中承载它。
  auto hr = _winContainer.CreateControl(L"shell.Explorer.2"); 
  // 查询指定的控件
  hr = _winContainer.QueryControl(__uuidof(IWebBrowser2), (void **)&_webBrowser2);
}
复制代码
  • loadURL实现
void Win32WebControl::loadURL(BSTR url) const
{
    VARIANT var;
    VariantInit(&var);
    var.vt = VT_BSTR;
    var.bstrVal = url;
    _webBrowser2->Navigate2(&var, NULL, NULL, NULL, NULL);
    VariantClear(&var);
}
复制代码

从c++的实现上观察到,核心在CAxWindow上,从网上找到的一些参考资料来看,win32平台的webview调用了ie的内核进行网页的加载渲染。

js调用c++

webview中的js代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>test</title>
</head>
<body>
    <div id="btn">call cpp</div>
    <script type="text/javascript" charset="UTF-8">
        var btn = document.getElementById('btn')
        btn.addEventListener('click', function() {
            window.external.testFunction(10,'hello test!');
        });
    </script>
</body>
</html>
复制代码

ie内核不支持js es6的特性,比如箭头函数()=>{}等...

js调用c++是通过window.external实现的,而window.external是一个逐渐被废弃的标准,早期是用来实现js和外部程序进行交互的。 以上的网页在普通浏览器运行,点击call cpp是会报错的,提示没有testFunction函数。 在shell.Explorer.2的环境中,是可以正常的。

js call cpp 核心逻辑流程:

1.设置js层window.external.xxx调用到c++层的目标对象。这一步非常重要,是建立通讯的一个桥梁。所有js external的函数调用,都会派发到设置的实例。

// 参数只要是IDispatch的实例即可
_winContainer.SetExternalDispatch(this);
复制代码

2.当js发生external调用时,会先回调步骤1设置的对象的GetIDsOfNames方法,在这一步,我们需要将调用的js函数名映射为一个id

至于为什么要将函数名映射为ID,猜测可能要和事件机制统一流程。

HRESULT STDMETHODCALLTYPE Win32WebControl::GetIDsOfNames(
    REFIID riid, 
    LPOLESTR *rgszNames, // js external 调用的函数名
    UINT cNames, 
    LCID lcid, 
    DISPID *rgDispId)
{
    if(wcscmp(rgszNames[0], L"testFunction") == 0){
        // 注意这里的rgDispId,将作为invoke的dispIdMember的入参
        // 从指针参数可以大概推测出,就是希望开发者控制改写这个参数
        *rgDispId = 199;
        return S_OK;
    }
    return E_NOTIMPL; // 这个返回值将不会调用invoke
}
复制代码
  1. 在这一步,才是我们真正要处理某个js调用真正逻辑的地方。也只有这里,我们才能拿到js传递的参数。
HRESULT STDMETHODCALLTYPE Win32WebControl::Invoke(
  DISPID dispIdMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  DISPPARAMS *pDispParams,
  VARIANT *pVarResult,
  EXCEPINFO *pExcepInfo,
  UINT *puArgErr)
{
    switch(dispIdMember)
    {
        case 199:{
            if (pDispParams->cArgs == 2)
            {
                VARIANTARG rgvarg0 = pDispParams->rgvarg[0];
                VARIANTARG rgvarg1 = pDispParams->rgvarg[1];
                // 参数是倒叙的,rgvarg包含一个union,需要根据type,检索正确的union类型
                rgvarg0.bstrVal; // hello test
                rgvarg1.intVal; // 10
                // todo testFunction logic
                return S_OK;
            }
            break;
        }
    }
}
复制代码

从整体设计上思考,将js的每一个external函数调用都视为了事件,在invoke汇总分发处理,所以在做js函数名id映射时,是否需要注意id和原有的发生冲突? 比如:DISPID_NAVIGATECOMPLETE2DISPID_COMMANDSTATECHANGE ...

关于js函数名和dispId的映射,可以参考思路

IDispatch.invoke详细参数

  • dispIdMember 标识成员。使用 GetIDsOfNames 或对象的文档来获取调度标识符。 在 ActiveX 客户端中,应使用 Invoke 来获取和设置属性值,或调用 ActiveX 对象的方法。dispIdMember 参数标识要调用的成员
  • wFlag
wFlag/value 参数含义
DISPATCH_METHOD/0x1 成员作为方法调用。如果属性具有相同的名称,则可以设置 this 和 DISPATCH_PROPERTYGET 标志。
DISPATCH_PROPERTYGET/0x2 该成员作为属性或数据成员进行检索。
DISPATCH_PROPERTYPUT/0x4 成员被更改为属性或数据成员。
DISPATCH_PROPERTYPUTREF/0x8 成员通过引用分配而不是值分配进行更改。此标志仅在属性接受对对象的引用时才有效。
  • pDispParams 指向包含参数数组、命名参数的参数 DISPID 数组以及数组中元素数的 DISPIDAMS 结构的指针。
  • pVarResult 指向要存储结果的位置的指针,如果调用者不期望结果,则为 NULL。如果指定了 DISPATCH_PROPERTYPUT 或 DISPATCH_PROPERTYPUTREF,则忽略此参数。 简单说:设置js external调用的返回值。
  • pExcepInfo 指向包含异常信息的结构的指针。如果返回 DISP_E_EXCEPTION,则应填写此结构。可以为 NULL。
  • puArgErr rgvarg 中第一个有错误的参数的索引。参数以相反的顺序存储在 pDispParams->rgvarg 中,因此第一个参数是数组中索引最高的参数。仅当结果返回值为 DISP_E_TYPEMISMATCH 或 DISP_E_PARAMNOTFOUND 时才返回此参数。该参数可以设置为空



目录
相关文章
|
2月前
|
IDE 测试技术 开发工具
Poco新增对cocos c++游戏的支持
Poco新增对cocos c++游戏的支持
|
4月前
|
JavaScript 前端开发 Serverless
函数计算只支持Node.js,我用C++写的程序怎么运行?
函数计算只支持Node.js,我用C++写的程序怎么运行?
90 1
|
24天前
|
算法 编译器 C++
【C++ 关键字的混合使用 】C++深度探索:auto、static、constexpr的交互影响与应用
【C++ 关键字的混合使用 】C++深度探索:auto、static、constexpr的交互影响与应用
30 0
|
1月前
|
小程序 JavaScript 前端开发
【微信小程序】--WXML & WXSS & JS 逻辑交互介绍(四)
【微信小程序】--WXML & WXSS & JS 逻辑交互介绍(四)
|
1月前
|
JavaScript 前端开发
javascript中的交互效果
javascript中的交互效果
|
2月前
|
前端开发 JavaScript
前端 JavaScript 与 HTML 怎么实现交互
前端 JavaScript 与 HTML 怎么实现交互
|
2月前
|
Rust 前端开发 JavaScript
Rust与JavaScript的跨语言交互:探索与实践
本文旨在探讨Rust与JavaScript之间的跨语言交互方法。我们将深入了解WebAssembly(Wasm)的角色,以及它如何使得Rust与JavaScript能够在Web应用中和谐共处。此外,我们还将介绍Rust与JavaScript的集成方式,包括Rust编译到Wasm、使用wasm-bindgen进行Rust与JavaScript的绑定,并通过实际案例展示如何实现两者之间的交互。
|
2月前
|
XML Android开发 数据格式
安卓和webview交互
安卓和webview交互
23 0
|
3月前
|
存储 监控 前端开发
JavaScript手册:公司员工电脑监控软件前端交互的代码设计
在当今信息时代,随着公司对员工电脑活动的监控需求不断增加,前端交互的代码设计变得尤为关键。本手册将深入探讨JavaScript编写的公司员工电脑监控软件监控代码,着重介绍如何设计能够在不引起怀疑的情况下,实现对员工电脑活动的细致监控。
226 2
|
4月前
|
存储 JavaScript C#
【傻瓜级JS-DLL-WINCC-PLC交互】8.DLL读写WINCC连接的PLC数据
【傻瓜级JS-DLL-WINCC-PLC交互】8.DLL读写WINCC连接的PLC数据
33 0
【傻瓜级JS-DLL-WINCC-PLC交互】8.DLL读写WINCC连接的PLC数据

热门文章

最新文章