cef3执行js后取返回值的方法

来源:赵克立博客 分类: Win32 标签:--发布时间:2018-04-02 10:15:30最后更新:2018-04-02 10:58:24浏览:6643
版权声明:
本文为博主原创文章,转载请声明原文链接...谢谢。o_0。
更新时间:
2018-04-02 10:58:24
温馨提示:
学无止境,技术类文章有它的时效性,请留意文章更新时间,如发现内容有误请留言指出,防止别人"踩坑",我会及时更新文章

cef中浏览和渲染不是一个线程所以执行js后是没有直接取返回值的,网上找啦很多方法都没有解决,下面提供一个方法可以实现大部分的使用情况,只要不是多线程并发执行js一般都没有问题

实现原理

cef可以绑定js来执行本地的c++代码,那么就可以在执行js代码的时候在外面包括一个已经绑定的js函数名字,这样代码执行完后会执行我们绑定的这个函数,代码返回值直接做为这个函数的参数传过来在应用程序中接收保存,保存后再当做c++的返回值返回,

问题解决

这里面涉及到多线程同步的操作,难点就在这里,执行js代码后,js函数再调用应用程序的代码这两个操作不是同步的是在两个线程中操作的,这就需要想个方法在执行js后让这个函数先不要返回,等取到返回值后再返回,首先想到的就是使用一个类的静态变量保存返回值,执行js后判断这个静态变量的值是不是空如果不是空就当成返回值返回,如果是空就一直while循环外加一个超时5秒返回空

这种方法不能使用多线程并发来执行js,并发执行js就导致返回值错乱


代码实现

声明保存返回值的静态变量和要绑定的静态函数名字

//静态的js返回值
static tstring s_JsReturnValue;
//设置返回值的方法名字
static tstring s_SetJsReturnValueFunc;
//初始化
tstring CCefWebKitUI::s_JsReturnValue = L"";
tstring CCefWebKitUI::s_SetJsReturnValueFunc = L"SetCppJsReturnValue";

绑定js函数名字到c代码

void CRenderAndBrowserApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefV8Context> context) {
        CefRefPtr<CV8ExtensionHandler> setJsHandler(new CV8ExtensionHandler());
        CefRefPtr<CefV8Value> setvalue = CefV8Value::CreateFunction(CCefWebKitUI::s_SetJsReturnValueFunc, setJsHandler);
        window->SetValue(CCefWebKitUI::s_SetJsReturnValueFunc, setvalue, V8_PROPERTY_ATTRIBUTE_NONE);
    }

在c代码中处理这个函数带过来的返回值

bool CV8ExtensionHandler::Execute(const CefString& name,
    CefRefPtr<CefV8Value> object,
    const CefV8ValueList& arguments,
    CefRefPtr<CefV8Value>& retval,
    CefString& exception) {
    if (name == "jscallc")
    {
        MessageBox(NULL, L"你调用啦c++中的一个信息框!", NULL, MB_OK);
        int32 nSum = 0;
        for (size_t i = 0; i < arguments.size(); ++i)
        {
            if (!arguments[i]->IsInt())
                return false;
            nSum += arguments[i]->GetIntValue();
        }
        retval = CefV8Value::CreateInt(nSum);
        return true;
    }
    else if (name == CCefWebKitUI::s_SetJsReturnValueFunc)
    {
        CCefWebKitUI::s_JsReturnValue = arguments[0]->GetStringValue();
        return true;
    }
    return true;
}

最后就是在执行js的时候让那个函数等待有返回值

tstring DuiLib::CCefWebKitWnd::RunJS(const CefString & jscode)
{
	CefRefPtr<CefBrowser> bro = client_browser_->browser_;
	if (bro) {
		//执行前先设置为空
		CCefWebKitUI::s_JsReturnValue = L"";
		tstring ljs = _T("(");
		tstring rjs = _T(")");
		tstring jsstr = jscode;
		jsstr = CCefWebKitUI::s_SetJsReturnValueFunc + ljs + jsstr + rjs;
		bro->GetMainFrame()->ExecuteJavaScript(jsstr, bro->GetMainFrame()->GetURL(), 0);
		//这个地方要加一个超时5秒,超时返回空字符串,不然可能会卡到这里
		time_t t_start, t_end;
		time(&t_start);
		while (true) {
			Sleep(100);
			if (!CCefWebKitUI::s_JsReturnValue.empty()) {
				break;
			}
			time(&t_end);
			if (difftime(t_end, t_start) > 5) {
				return L"";
			}
		}
		tstring revalue = CCefWebKitUI::s_JsReturnValue;
		CCefWebKitUI::s_JsReturnValue = L"";
		return revalue;
	}
}

注意事项

1、如果执行的js有错误,那么就会超时后返回,这种情况会卡住界面,

1、执行一个声明变量的语句并没有返回值,直接这样执行就会报错,如这样的代码 var a='';  包装后会变成这样的代码    setValue(var a='');  执行就会报错,解决方法写两个执行js的方法一个是有返回值的一个是没有返回值的,这样执行js的时候根据有没有返回值调用不同的函数


微信号:kelicom QQ群:215861553 紧急求助须知
Win32/PHP/JS/Android/Python