duilib + cef简单浏览器的demo2--c++和js交互

参考连接:

https://bitbucket.org/chromiumembedded/cef/wiki/Home

demo下载地址:

http://download.csdn.net/detail/shuaixingrumo/9560822


前一篇博文讲述了在duilib中怎么使用cef3, 这篇我们来探讨下怎么在duilib中使用cef3, 让c++和js交互。

首先需要提下, cef3中, render和browser是俩个进程, 所以c++和js交互的时候, 需要用到进程的通信, 方法我就不多说了, 管道, 共享内存之类的, 下面简单介绍下我在demo中用到的通信方法, 大家可以根据需要选择适合自己项目的方法:

1、render往界面传送消息, 我用的是比较简单的方法, 向界面窗口发送WM_COPYDATA消息, 我们可以把数据格式化为一个json串来进行数据交互,然后在duilib窗口函数的HandleCustomMessage中处理这个消息传过来的数据

2、从duilib往render进程发送消息, 用cef3中CefBrowser的SendProcessMessage方法发送CefProcessMessage给render进程, 然后在render进程中的OnProcessMessageReceived中来处理从duilib窗口中发送过来的消息。

首先, 介绍一种简单的调用js函数的方式:

[cpp]  view plain  copy
  1. m_handler->GetBrowser()->GetMainFrame()->ExecuteJavaScript(strJSFuncName, m_handler->GetBrowser()->GetMainFrame()->GetURL(), 0);  

使用CefBrowser的GetMainFrame()获取browser的mainframe, 再调用他的ExecuteJavaScript方法来调用页面的js函数, 此种方法比较简单, 就不多介绍了, 大家可以自己去实验


想要c++和js交互, 我们需要实现CefRenderProcessHandler的OnContextCreated、OnBrowserDestroyed和OnProcessMessageReceived这三个方法, 如果要和js函数交互, 还需要实现CefV8Handler的Execute方法, 这些方法的用途如下:

OnContextCreated:在创建完render的context后调用, 我们可以在这里去绑定一些js的函数, 变量

OnBrowserDestroyed: browser销毁的时候调用, 我们可以用来做一些清理工作

OnProcessMessageReceived: 用来接收处理传给render进程的消息

Execute:在OnContextCreated中绑定的js函数执行时, 会调用这个函数, 我们可以在这个函数中处理这些js函数

为了demo简单, 操作方便, 我直接在SImpleApp中实现了这些方法, 具体完整的实现代码参看demo中的代码

我们先看OnContextCreated的实现:

[cpp]  view plain  copy
  1. void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser,  
  2.                               CefRefPtr<CefFrame> frame,  
  3.                               CefRefPtr<CefV8Context> context)  
  4. {  
  5.     CEF_REQUIRE_RENDERER_THREAD();  
  6.     m_js_callback_context = NULL;  
  7.     m_js_callback_func = NULL;  
  8.     m_hWndMain = FindWindow(L"main_wnd_class", NULL);  
  9.     CefRefPtr<CefV8Value> object = context->GetGlobal();  
  10.     CefRefPtr<CefV8Handler> myhandler = this;  
  11.     CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(L"testfunc", myhandler);  
  12.     CefRefPtr<CefV8Value> func2 = CefV8Value::CreateFunction(L"testfunc2", myhandler);  
  13.     object->SetValue(L"myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);  
  14.     object->SetValue(L"test2", func2, V8_PROPERTY_ATTRIBUTE_NONE);  
  15.     object->SetValue(L"registercb", CefV8Value::CreateFunction(L"registercb", myhandler), V8_PROPERTY_ATTRIBUTE_NONE);  
  16. }  

我们这里面绑定了js函数, myfunc()、test2()、registercb()和做了一些必要的初始化工作,

首先, 我们我们需要获取一个render的context窗口对象object, 用它来去绑定js对象和函数, 先介绍下如何绑定js对象, 以string类型为例:

代码如下:

[cpp]  view plain  copy
  1. CefRefPtr<CefV8Value> str = CefV8Value::CreateString("test string");  
  2.  object->SetValue("strobj", str, V8_PROPERTY_ATTRIBUTE_NONE);  

我们把变量strobj的值设置为test string.

关于绑定js函数, 我们首先需要获取一个CefV8Handler对象, 这样, 在绑定的js函数执行时, 我们才知道要去哪个Execute方法中处理,实现代码:

[cpp]  view plain  copy
  1. CefRefPtr<CefV8Handler> myhandler = this;  
获取一个CefV8Handler的对象,因为我们在一个类中实现了这些方法, 所以取该类的this指针就行, 如果你单独创建了一个类继承CefV8Handler及实现他的Execute方法, 你需要创建那个类的对象

[cpp]  view plain  copy
  1. CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(L"testfunc", myhandler);  
一个名为testfunc的c++函数对象, 我们一会用它去和js函数绑定, 在CefV8Handler的Execute中用名称testfunc来区分哪个js函数被调用了

[cpp]  view plain  copy
  1. object->SetValue(L"myfunc", func, V8_PROPERTY_ATTRIBUTE_NONE);  
把前面创建的func函数对象和js函数myfunc绑定

然后我们去CefV8Handler的Execute方法中处理, 当js函数myfunc执行时, c++中需要处理的一些操作

[cpp]  view plain  copy
  1. bool SimpleApp::Execute(const CefString& name,  
  2.                        CefRefPtr<CefV8Value> object,  
  3.                        const CefV8ValueList& arguments,  
  4.                        CefRefPtr<CefV8Value>& retval,  
  5.                        CefString& exception)  
  6. {  
  7.     if(name.compare(L"testfunc") ==0)  
  8.     {  
  9.         COPYDATASTRUCT cds;  
  10.         const wchar_t* lpData = name.c_str();  
  11.         cds.lpData = (LPVOID)lpData;  
  12.         cds.cbData = (wcslen(lpData) + 1)*sizeof(WCHAR);  
  13.         if(m_hWndMain && IsWindow(m_hWndMain))  
  14.             ::SendMessage(m_hWndMain, WM_COPYDATA, NULL, (LPARAM)&cds);  
  15.         return true;  
  16.     }  
  17.     else if(name.compare(L"testfunc2") == 0)  
  18.     {  
  19.         if(arguments.size() == 1 && arguments[0]->IsString())  
  20.         {  
  21.             COPYDATASTRUCT cds;  
  22.             std::wstring str;  
  23.             str = L"function name: ";  
  24.             str += name;  
  25.             str += L"   arg: ";  
  26.             str += arguments[0]->GetStringValue();  
  27.             LPCWSTR lpData = str.c_str();  
  28.             cds.lpData = (LPVOID)lpData;  
  29.             cds.cbData = (wcslen(lpData) + 1)*sizeof(WCHAR);  
  30.             if(m_hWndMain && IsWindow(m_hWndMain))  
  31.                 ::SendMessage(m_hWndMain, WM_COPYDATA, NULL, (LPARAM)&cds);  
  32.   
  33.             return true;  
  34.         }  
  35.     }  
  36.     else if(name.compare(L"registercb") == 0)  
  37.     {  
  38.         if(arguments.size() == 1 && arguments[0]->IsFunction())  
  39.         {  
  40.             m_js_callback_func = arguments[0].get();  
  41.             m_js_callback_context = CefV8Context::GetCurrentContext();  
  42.             CefV8ValueList args;  
  43.             args.push_back(CefV8Value::CreateString(L"register call back function"));  
  44.             args.push_back(CefV8Value::CreateInt(0));  
  45.             m_js_callback_func->ExecuteFunctionWithContext(m_js_callback_context, NULL, args);  
  46.         }  
  47.         return true;  
  48.     }  
  49.     return false;  
  50. }  

首先, 我们根据参数name来判断哪个js函数被调用了, name的值是前面我们创建函数对象的名称, 参考前面的代码

如果我们要把数据传给duilib窗口去处理, 我们需要使用WM_COPYDATA消息去传递数据, 我们先用FindWindow找到我们duilib窗口, 然后把需要的数据通过EM_COPYDATA发送出去, 具体实现参看代码。

下面我们要继续介绍如何注册一个js的回调函数, 来让我们在需要的时候来调用这个回调函数:

首先, 我们绑定一个js函数对象:

[cpp]  view plain  copy
  1. object->SetValue(L"registercb", CefV8Value::CreateFunction(L"registercb", myhandler), V8_PROPERTY_ATTRIBUTE_NONE);  
在js代码中注册一个回调函数:

[javascript]  view plain  copy
  1. function cbFunc(arg0, arg1)  
  2. {  
  3. alert("arg0:"+arg0+"  arg1:"+arg1);  
  4. }  
  5. window.registercb(cbFunc);  

再Execute方法中保存这个回调函数对象, 等待我们需要的时候调用:

[cpp]  view plain  copy
  1. m_js_callback_func = arguments[0].get();  
  2. m_js_callback_context = CefV8Context::GetCurrentContext();  

这里需要记得保存当前的context

等我们需要调用这个回调函数的时候, 从duilib窗口或这browser中传递一个消息到render进程中:

[cpp]  view plain  copy
  1. CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("callback_func");  
  2. CefRefPtr<CefListValue> args = msg->GetArgumentList();  
  3. args->SetString(0, "call js call back function");  
  4. args->SetInt(1, 10);  
  5. m_handler->GetBrowser()->SendProcessMessage(PID_RENDERER, msg);  
从代码中我们可以看出, 我们创建了一个callback_func的消息, 同时设置了一个string类型的参数和一个int类型的参数, 然后把这个消息发送给render进程, 这里小提一下, 如果用这种方式, 从render进程发送给browser进程消息, 我们可以在render进程中创建一个CefProcessMessage的消息, 然后把PID_RENDERER改为PID_BROWSER

接下来, 我们在render进程中的OnProcessMessageReceived处理这个消息:

[cpp]  view plain  copy
  1. bool SimpleApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,  
  2.                                         CefProcessId source_process,  
  3.                                         CefRefPtr<CefProcessMessage> message)  
  4. {  
  5.     if(message->GetName().compare(L"callback_func") == 0)  
  6.     {  
  7.         if(m_js_callback_func)  
  8.         {  
  9.             CefV8ValueList args;  
  10.             CefString str = message->GetArgumentList()->GetString(0);  
  11.             args.push_back(CefV8Value::CreateString(str));  
  12.             args.push_back(CefV8Value::CreateInt(message->GetArgumentList()->GetInt(1)));  
  13.             m_js_callback_func->ExecuteFunctionWithContext(m_js_callback_context, NULL, args);  
  14.         }  
  15.         return true;  
  16.     }  
  17.     return false;  
  18. }  

从代码可以看出, 我们用消息名来区分消息, 然后创建一个CefV8ValueList对象, 用来保存传给js函数的参数, 我们把之前放在消息中的字符串类型参数和int类型参数取出, 放入参数对象中,然后执行ExecuteFunctionWithContext方法来调用js回调函数。


好了, 关于cef3中c++和js交互部分就简单介绍这些了, 小伙伴们可以根据文章开头的连接去学习cef3更多的使用技巧, 如果有不对和需要补充的地方, 欢迎大家交流, 下面给出我写的用来测试的html代码:

[html]  view plain  copy
  1. <!doctype html>  
  2. <html lang="en">  
  3. <head>  
  4.     <meta charset="UTF-8">  
  5.     <title>Document</title>  
  6.     <script type="text/javascript">  
  7.         function testfunc()  
  8.         {  
  9.             window.myfunc();  
  10.         }  
  11.         function testfunc2(str){  
  12.             window.test2(str);  
  13.         }  
  14.         function regfunc(){  
  15.             alert("regfunc");  
  16.         }  
  17.         function registerfunc(){  
  18.             window.registercb(regfunc);  
  19.             alert('register function regfunc successfully, click "call js" to call regfunc');  
  20.         }  
  21.     </script>  
  22. </head>  
  23. <body>  
  24.     <p>test</p>  
  25.     <input type="button" value="call myfunc, no arg" onclick="testfunc()" /><br/>  
  26.     <input type="button" value='call test2 func, arg="a string value"' onclick='testfunc2("a string value")' /><br/>  
  27.     <input type="button" value="register a js func: regfunc" onclick="registerfunc()" />  
  28. </body>  
  29. </html>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值