在编译好libCef文件之后,我们需要对文件目录做一些整理。
使用vs2019创建Qt项目,这里我就略过。
最后目录格式如下所示:
│ QCefWindow.sln
│ QSimpleCef.vcxproj
│ QSimpleCef.vcxproj.filters
│
├─bin
│ ├─debug
│ │ │ chrome_100_percent.pak
│ │ │ chrome_200_percent.pak
│ │ │ ...
│ │
│ └─release
│ │ cef_sandbox.lib
│ │ chrome_100_percent.pak
│ │ chrome_200_percent.pak
│ │ ...
│
├─include
│ │ cef_accessibility_handler.h
│ │ cef_api_hash.h
│ │ cef_string_visitor.h
│ │ ...
│ │
├─res
│ │ app.manifest
│ │ qtwidgetsapplication1.qrc
│ │
│ ├─images
│ └─ui
│ qtwidgetsapplication1.ui
│
└─source
main.cpp
qtwidgetsapplication1.cpp
qtwidgetsapplication1.h
...
在这个简单的demo有几处重点需要解释下:
/**
* 初始化QT以及CEF相关
*/
int init_qt_cef(int& argc, char** argv)
{
const HINSTANCE h_instance = static_cast(GetModuleHandle(nullptr));
CefMainArgs main_args(h_instance);
CefRefPtr app(new SimpleApp); //CefApp实现,用于处理进程相关的回调。
const int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
if (exit_code >= 0)
{
return exit_code;
}
// Parse command-line arguments for use in this method.
CefRefPtr command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
// 设置配置
CefSettings settings;
settings.multi_threaded_message_loop = true; //多线程消息循环
settings.log_severity = LOGSEVERITY_DEBUG; //日志
settings.no_sandbox = true; //沙盒
if (command_line->HasSwitch("enable-chrome-runtime")) {
// Enable experimental Chrome runtime. See issue #2969 for details.
settings.chrome_runtime = true;
}
bool result = CefInitialize(main_args, settings, app.get(), nullptr);
return -1;
}
因为libCef是多进程架构,所以下面的这条语句就会进来多次。
比如:看我的机器上的进程
这里的SimpleApp是个什么东西,文档解释说提供进程级别的回调。好,我也是看的一脸懵逼。不过我暂且不着急去理解,我想随着深入的去做,我后面在来解释。
CefRefPtr app(new SimpleApp); //CefApp实现,用于处理进程相关的回调。
const int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
if (exit_code >= 0)
{
return exit_code;
}
设置多线程消息循环,这样是为了让libCef可以集成到我们的Qt框架。
settings.multi_threaded_message_loop = true; //多线程消息循环
从这里可以发现一个很有趣的现象,那就是这个demo中会有两个UI线程。
所以如果你去看libCef的代码,你就会发现很多CefCurrentlyOn(TID_UI)
这种语句的判断,就是在检查是否是在libCef的UI 线程环境中。
如果不是就会Post Task过去,通知libCef UI线程去做。因为我们不能直接Qt的UI线程去修改libCef的UI,多线程操作UI是不安全的。
Tip:
这里我们使用了libCef的多线程消息循环,在最后libCef退出的时候,不能在调用
CefQuitMessageLoop()
退出消息
日志的设置是非常重要的,我把它设置为DEBUG级别的。会在运行目录下生成一个debug.log文件。
settings.log_severity = LOGSEVERITY_DEBUG; //日志
因为是MD模式编译,所以沙盒就没法用了。更多信息参考之前的文章
settings.no_sandbox = true; //沙盒
其实这两个类并不是我自己的写的,而是我从libCef中的Demo中直接复制过来的。
我们先看simple_handler.h从头文件来看,就是继承了各种父类,然后去重写它们的虚函数
比如:
CefClient提供访问Browser实例的回调接口
CefLifeSpanHandler对Browser生命周期(Browser Life Span),比如:browser create ,browser destory
CefDisplayHandler 对浏览器上面的ui变化,比如:title change, address change…
CefLoadHandler浏览器上面的加载状态的变化,比如:load start, load end, load error…
class SimpleHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
CefApp接口提供了不同进程的可定制回调函数,比如:渲染进程,GPU进程的回调事件
class SimpleApp : public CefApp, public CefBrowserProcessHandler
这个暂且就解释到这里,随着后面的深入我在解释。
其实我个人感觉,如果只是在应用层借助libCef开发,我们主要做得事情就是继承各种class,实现虚函数,然后被框架调用。
说的简单点:就是实现各种回调函数
最后,我把源码 已经上传到gitee了,有兴趣的同学可以下载跑下看看效果。
项目地址:https://gitee.com/liushixiong/qcef-window
下一节,我会说明 Check failed: !IsCefShutdown(). Object reference incorrectly错误原因