中调用,而base库的UI线程消息循环是封装好的。这里首先说一下定制base库消息循环的方法。在WinMain入口函数里调用UI消息循环的代码如下: {
MainThread thread; // 创建主线程
thread.RunOnCurrentThreadWithLoop(nbase::MessageLoop::kUIMessageLoop); // 执行主线程循环 }
在RunOnCurrentThreadWithLoop方法的第二个参数里可以指定一个消息分派器指针dispatcher,dispatcher继承自nbase::Dispatcher。base库中的UI消息循环代码如下: PreProcessMessage(msg);
if (state_->dispatcher) {
if (!state_->dispatcher->Dispatch(msg)) state_->should_quit = true; } else
{
TranslateMessage(&msg); DispatchMessage(&msg); }
PostProcessMessage(msg);
如果我们指定了RunOnCurrentThreadWithLoop方法的第二个参数,就不会调用原本的消息循环了,所以可以在这个dispatcher里定制消息循环。我实现
CefMessageLoopDispatcher类并重写Dispatch接口。 BOOL CefMessageLoopDispatcher::IsIdleMessage(const MSG* pMsg) {
switch (pMsg->message) {
case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: case WM_PAINT: return FALSE; }
return TRUE; }
bool CefMessageLoopDispatcher::Dispatch(const MSG &msg) {
static BOOL bDoIdle = TRUE;
TranslateMessage(&msg); DispatchMessage(&msg);
if (IsIdleMessage(&msg)) {
bDoIdle = TRUE; }
while (bDoIdle
&& !::PeekMessage(const_cast<MSG*>(&msg), NULL, 0, 0, PM_NOREMOVE)) {
CefDoMessageLoopWork();
bDoIdle = FALSE; }
return true; }
在定制消息循环里,如果判断当前消息队列为空并且刚才处理的消息不会指定的几个消息,就去调用CefDoMessageLoopWork函数。WM_PAINT、
WM_MOUSEMOVE等消息的处理比较复杂,所以不在这里调用CefDoMessageLoopWork函数
这个方法基本可以使用,但是还存在一些问题,这里CefDoMessageLoopWork函数的调用机制还不够好,Cef界面不够顺畅,而且因为Cef与项目base库的冲突,导致在程序结束时有些问题。这个方法有待优化
第二种方法
CefSettings结构体的multi_threaded_message_loop(多线程消息循环)为false时,可以调用CefRunMessageLoop或
者CefDoMessageLoopWork函数来触发Cef消息循环,这时浏览器进程的UI线程就是调用CefRunMessageLoop或者CefDoMessageLoopWork函数的线程。如果CefSettings结构体的multi_threaded_message_loop为true时。浏览器进程的UI线程是另外的线程。设置
multi_threaded_message_loop为true则使用多线程消息循环。
通过对比Cef Demo的多线程消息循环代码,可以确定在NIM项目中直接开启多线程消息循环,不需要修改现有消息循环代码就可以正常使用Cef了。不过需要注意的是,使用多线程消息循环后某些函数就无法使用了,比如
CreateBrowserSync,这种函数要求必须在Cef的UI线程调用。
另外,在很多版本的Cef里,如果开启了多线程消息循环,会导致程序在结束时触发中断,这属于Cef的bug,不过在release版本的Cef中没有问题。应该在项目中使用这个方法。不过使用了多线程消息循环后,很多Cef对象触发的回调函数,都是在Cef的UI线程而不是我们的UI线程,所以这时操作我们的UI线程就比较麻烦,要注意一些多线程问题,尽量把操作转发到我们的UI线程,不转发的话必须确