我正在Visual Studio 2013(C++)和Windows 8.1上测试一个CEF3程序。
我想处理从JavaScript调用的本机函数。但是,在执行本机函数时,浏览器会冻结。PostTask也无效。
在使用CreateThread(Win32 API)创建线程的情况下,在其他线程中获取Cef8Value时会发生错误。
没有什么好的异步处理函数的方法吗?
CefSettings settings;
settings.single_process = true;
void App::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Retrieve the context's window object.
CefRefPtr<CefV8Value> object = context->GetGlobal();
CefRefPtr<CefV8Handler> handler = new AppExtensionHandler(this);
object->SetValue("execNative",
CefV8Value::CreateFunction("execNative", handler),
V8_PROPERTY_ATTRIBUTE_NONE);
}
bool AppExtensionHandler::Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception)
{
CefRefPtr<CefBrowser> browser = CefV8Context::GetCurrentContext()->GetBrowser();
if (!browser.get()) return false;
if (name == "execNative") {
Sleep(10000); // heavy process
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript(L"result(true)", frame->GetURL(), 0);
}
return true;
}
问题是javascript回调发生在CEF客户端的渲染进程上。这个过程直接负责所有的用户交互、点击、JS执行等等。即使你发布到另一个线程(在同一个过程中),看起来也没有太大的区别。
您想要做的是将其发送到浏览器进程并在那里进行处理。CEF3传真涵盖了这种通信,以防您以前不需要这样做:
[…]如何在浏览器和CEF3中的渲染进程之间发送信息
为了动态地提供信息,使用与特定
CefBrowser
实例相关联并且使用CefBrowser::SendProcessMessage()
方法发送的进程消息(CefProcessMessage
)。[…]从渲染进程发送到浏览器进程的消息将到达CefClient::OnProcessMessageReceived()
。[…]
在Adobe Brackets Shell(一个相当流行的使用CEF3的开源WebIDE)中,这似乎是一件大事——他们的V8扩展指南很好地介绍了这一点。
无论何时调用本机函数,都会调用
AppShellExtensionHandler::Execute()
。此代码在呈现过程中运行,因此这里只应执行最琐碎的扩展代码。对于Brackets,此处仅处理getElapsedMilliseconds()
。所有其他调用都通过CefProcessMessage
传递给浏览器进程。
这里提到的AppShellExtensionHandler
相当于您的AppExtensionHandler
。我强烈建议仔细阅读他们的代码以进行扩展处理——这做得非常优雅公平警告:虽然Brackets.io是一家开源企业,但我在专业上与Adobe有关系
希望这能有所帮助——干杯!
看看这篇文章。它就像一个符咒。解决了我所有的JS到本机的问题,并使用结构化的参数传递方法。。它也正好解决了你所面临的问题。。。你只需要创建一个用户线程(Task)来处理后台的事情。。。
JS通过V8Context转换为本机-异步!
我自己测试过,效果非常好。。除非我错过了什么。。。
虽然这篇文章描述了C#,但应该非常直接地将其翻译为C++