前一篇文章介绍过CEF在WIN32程序中嵌入chrome内核浏览器的例子:http://blog.csdn.net/mfcing/article/details/43973377
这里介绍的是嵌入浏览器后,网页的JS脚本函数与C++代码的交互,这个很多地方都用得到。比如:音乐播放器里网页上的播放,客户端资源中心里的资源下载……
首先需要重写CefRenderProcessHandler的OnContextCreated接口,为什么呢?学习CEF库的使用必须仔细阅读他的头文件里的注视部分:
// Called immediately after the V8 context for a frame has been created. To // retrieve the JavaScript 'window' object use the CefV8Context::GetGlobal() // method. V8 handles can only be accessed from the thread on which they are // created. A task runner for posting tasks on the associated thread can be // retrieved via the CefV8Context::GetTaskRunner() method. /// /*--cef()--*/ virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) {}这个接口实在chrome的V8引擎创建后调用的,在这里我们需要将JS里面调用的函数和C++的执行函数关联起来,这样JS就可以“执行”C++代码了。
我们的函数都是定义在window对象中的(不知道JS中这个是不是叫对象),根据注视我们需要GetGlobal获取这个对象。
我的代码是这样的:
CefRefPtr<CefV8Value> window = context->GetGlobal(); CefRefPtr<CefV8Accessor> myV8Acc = new CCefV8Accessor; CefRefPtr<CefV8Value> val = CefV8Value::CreateString(L"Application"); CefString cefException; myV8Acc->Set(L"name", window, val, cefException); CefRefPtr<CefV8Value> pObjApp = CefV8Value::CreateObject(myV8Acc); window->SetValue(L"Application", pObjApp, V8_PROPERTY_ATTRIBUTE_NONE); CefRefPtr<CefV8Handler> myV8handle = new CCefV8Handler(); CefRefPtr<CefV8Value> myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle); static_cast<CCefV8Handler*>(myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState); pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE); myFun = CefV8Value::CreateFunction(L"OneClickInstall", myV8handle); static_cast<CCefV8Handler*>(myV8handle.get())->AddFun(L"OneClickInstall", &CChromeJsCallback::JsOneKeyInstall); pObjApp->SetValue(L"OneClickInstall", myFun, V8_PROPERTY_ATTRIBUTE_NONE); myFun = CefV8Value::CreateFunction(L"DownLoadFile", myV8handle); static_cast<CCefV8Handler*>(myV8handle.get())->AddFun(L"DownLoadFile", &CChromeJsCallback::JsDownloadFile); pObjApp->SetValue(L"DownLoadFile", myFun, V8_PROPERTY_ATTRIBUTE_NONE);
创建函数对象,并将JS函数绑定到C++函数指针上面的过程是重点详细介绍下
CefRefPtr<CefV8Handler> myV8handle = new CCefV8Handler(); CefRefPtr<CefV8Value> myFun = CefV8Value::CreateFunction(L"SetAppState", myV8handle); static_cast<CCefV8Handler*>(myV8handle.get())->AddFun(L"SetAppState", &CChromeJsCallback::JsSetAppState); pObjApp->SetValue(L"SetAppState", myFun, V8_PROPERTY_ATTRIBUTE_NONE);
最上面那段代码中,我们又添加了三个函数 SetAppState、OneClickInstall、DownLoadFile到window.application对象上面。
C++代码中,这三个函数是这样写的:
//JS函数,在其他进程中调用 static bool JsSetAppState(const CefV8ValueList& argList, CefRefPtr<CefV8Value>& retValue); static bool JsOneKeyInstall(const CefV8ValueList& argList, CefRefPtr<CefV8Value>& retValue); static bool JsDownloadFile(const CefV8ValueList& argList, CefRefPtr<CefV8Value>& retValue);
这里要注意:CEF可以使用单进程和多进程模式,我程序里使用的是多进程模式,因此V8引擎的执行是在渲染引擎里的,不要尝试在这里直接去对界面进行处理。界面进程和渲染进程是分开的,数据的话用共享内存来做,界面更新发消息来做。
C++调用JS函数相对简单多了,因为CEF有接口可以直接使用CefFrame::ExecuteJavaScript,看看注释:
// Execute a string of JavaScript code in this frame. The |script_url| // parameter is the URL where the script in question can be found, if any. // The renderer may request this URL to show the developer the source of the // error. The |start_line| parameter is the base line number to use for error // reporting. /// /*--cef(optional_param=script_url)--*/ virtual void ExecuteJavaScript(const CefString& code, const CefString& script_url, int start_line) =0;
void CChromeBrowserUI::ExecuteJavascript( const wstring& strCode ) { if ( m_pWebBrowser.get() ) { CefRefPtr<CefFrame> frame = m_pWebBrowser->GetMainFrame(); if ( frame.get() ) { CefString strCode(strCode.c_str()), strUrl(L""); frame->ExecuteJavaScript(strCode, strUrl, 0); } } }注意:函数名和参数名需要用单引号分隔。
我的字符串格式化部分:
CString strJsCode; strJsCode.Format(L"setInstallStatus('%s','%s','%d');", lpData->strId.c_str(), strStatus, nPercent);