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 时才返回此参数。该参数可以设置为空



目录
相关文章
|
6月前
|
IDE 测试技术 开发工具
Poco新增对cocos c++游戏的支持
Poco新增对cocos c++游戏的支持
|
6月前
|
JavaScript 前端开发 Serverless
函数计算只支持Node.js,我用C++写的程序怎么运行?
函数计算只支持Node.js,我用C++写的程序怎么运行?
112 1
|
2月前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
2月前
|
C++
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
|
4月前
|
JavaScript 前端开发 API
Node中的AsyncLocalStorage 使用问题之Node.js将 JavaScript 层的 nativeHooks 注册到 C++ 层的问题如何解决
Node中的AsyncLocalStorage 使用问题之Node.js将 JavaScript 层的 nativeHooks 注册到 C++ 层的问题如何解决
|
5月前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
5月前
|
JavaScript 前端开发 Android开发
kotlin开发 webview如何在收到JS调用后,native返回数据给到JS
这段内容描述了在Hybrid App开发中,使用Kotlin的Compose构建的Web视图(WebView)如何通过JsBridge实现JavaScript与原生代码的交互
|
4月前
|
Web App开发 JavaScript 前端开发
Android端使用WebView注入一段js代码实现js调用android
Android端使用WebView注入一段js代码实现js调用android
119 0
|
6月前
|
编解码 JavaScript 前端开发
【专栏】介绍了字符串Base64编解码的基本原理和在Java、Python、C++、JavaScript及Go等编程语言中的实现示例
【4月更文挑战第29天】本文介绍了字符串Base64编解码的基本原理和在Java、Python、C++、JavaScript及Go等编程语言中的实现示例。Base64编码将24位二进制数据转换为32位可打印字符,用“=”作填充。文中展示了各语言的编码解码代码,帮助开发者理解并应用于实际项目。
148 1
|
6月前
|
编译器 C++
C/C++中的逻辑运算与汇编指令的交互
C/C++中的逻辑运算与汇编指令的交互
42 0