QT集成CEF07-理解CEF多进程
本小节主要理解CEF的多进程模型,为后面的JavaScript与C++ 通信做一些知识储备,顺便再次梳理一下CEF的启动流程。
1.任务管理器观察多进程
CEF基于Chromium,是多进程模型。怎么理解多进程?启动一个Chrome浏览器,多打开几个网站,然后再打开资源管理器查看Chrome的进程,现在对任务管理器做一些调整,目的是查看每个进程所启动的命令行以及命令行所携带的参数。如下图:勾选 “PID”和“命令行”
此时可以看到Chrome浏览器开启的进程:
可以看到Chrome浏览器启动了多个进程,2852
这个进程的命令行后面并没有携带任何其它参数,它就是browser进程 ,而其它进程如 4296
这个进程携带了 --type=renderer ...
多个参数,它是 render进程
打开CEF的自带的cefsimple 应用也能看到多个进程的存在。那么这些进程是怎么启动的?都有哪些作用?
2.Chromium的进程分类
- browser 没有type参数时默认为browser进程
- renderer 渲染进程
- plugin 插件进程
- ppapi-broker
- ppapi
- sandbox-ipc
- utility
- zygote
- gpu-process
这里我们必须要理解的是 browser 进程 和 render进程
browser进程: 处理窗口创建、窗口绘制、网络交互以及大部分的主要逻辑。browser进程通常就是宿主进程。
render进程: 每个页面都是运行在自己的进程里,这些进程我们称之为render进程。render进程会在窗口中渲染出web页面(引用了CSS,JavaScript,图片等的HTML文件)。
需要注意的是,browser 进程中会进行窗口绘制,并不是指绘制HTML内容,而是承载网页内容的那个窗体壳,同样render进程也不是用来创建窗体的进程。
3.进程怎么启动
查看sefsimple示例程序的入口函数:
// ...其它省略
int exit_code = CefExecuteProcess(main_args, nullptr, sandbox_info);if (exit_code >= 0) {return exit_code;
}
// ...其它省略
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
CefSettings settings;
settings.no_sandbox = true;
CefRefPtr<SimpleApp> app(new SimpleApp);
// 初始化CEF
CefInitialize(main_args, settings, app.get(), sandbox_info);
// 开启消息循环
CefRunMessageLoop();
// 释放CEFCefShutdown();
// ...其它省略
创建进程是用 CefExecuteProcess
函数创建的。这个函数在 libcef_dll_wrapper.cc
中,它内部又调用了cef_execute_process方法(libcef_dll.cc),cef_execute_process又调用了libcef/browser/context.cc文件内实现的CefExecuteProcess方法。源码中有这样一句代码:
int CefExecuteProcess(const CefMainArgs& args,CefRefPtr<CefApp> application,void* windows_sandbox_info) {base::CommandLine command_line(base::CommandLine::NO_PROGRAM);// 省略...// If no process type is specified then it represents the browser process and// we do nothing.std::string process_type =command_line.GetSwitchValueASCII(switches::kProcessType);if (process_type.empty())return -1;CefMainDelegate main_delegate(application);// Execute the secondary process.// 省略...content::ContentMainParams params(&main_delegate);params.instance = args.instance;// 省略...params.argc = args.argc;params.argv = const_cast<const char**>(args.argv);return content::ContentMain(params);
#endif
它分析了命令行参数,提取”type”参数,如果为空,说明是Browser进程,返回-1,sefsimple 入口函数继续向后执行,创建了 SimpleApp
它实现了CefApp
接口,给了我们控制CEF的入口,接着就进行了初始化。初始化完毕之后,回调了SimpleApp::OnContextInitialized()
方法,在这个方法中我们创建了 浏览器,并用到了 SimpleHandler
而它实现了 CefClient
如果”type”参数不为空,做一些判断,最后调用了content::ContentMain方法,创建子进程,直到这个方法结束,子进程才结束。
编译后的可执行文件为"sefsimple.exe", 在进程管理器中可以看到每个进程都是从 sefsimple.exe开始的,只不过是参数不同而已。
观察CefExecuteProcess 方法的第二个参数,它是一个CefApp 类型的,在我们自己的例子程序和sefsimple 中都传递了NULL,实际上它也可以为它传递一个 CefApp对象。
分析完这些,现在我们清楚了,Browser进程,需要CefApp(SimpleApp实现了这个接口)和CefClient(SimpleHandler实现了这个接口)。而Renderer进程只要CefApp。 前面我们自己编写的示例和sefsimple 都传递了NULL。
现在再打开示例程序cefclient的入口函数:
int RunMain(HINSTANCE hInstance, int nCmdShow) {// ...省略CefMainArgs main_args(hInstance);void* sandbox_info = nullptr;// Create a ClientApp of the correct type.CefRefPtr<CefApp> app;ClientApp::ProcessType process_type = ClientApp::GetProcessType(command_line);if (process_type == ClientApp::BrowserProcess)app = new ClientAppBrowser();else if (process_type == ClientApp::RendererProcess)app = new ClientAppRenderer();else if (process_type == ClientApp::OtherProcess)app = new ClientAppOther();// Execute the secondary process, if any.int exit_code = CefExecuteProcess(main_args, app, sandbox_info);if (exit_code >= 0)return exit_code;// ...省略return result;
}
这段代码上我们看到了这段代码根据 进程类型,创建了不同的CefApp,看看 ClientAppBrowser
(创建browser进程用)和 ClientAppRenderer
(创建render进程用) 有什么不一样的?
// client_app_browser.h
class ClientAppBrowser : public ClientApp, public CefBrowserProcessHandler {//...
}// client_app_renderer.h
class ClientAppRenderer : public ClientApp, public CefRenderProcessHandler {//...
}
可以看到明显的:
browser进程 需要 CefApp和 CefClient, CefApp要提供CefBrowserProcessHandler 接口,
render进程 只需要 CefApp ,CefApp要提供 CefRenderProcessHandler 接口。
CefBrowserProcessHandler
接口用来处理 browser进程 的一些回调, CefRenderProcessHandler
接口用来处理 render进程 的一些回调。
我们自己的例子和 sefsimple 示例程序没有针对Render进程做特别处理,因为 CefApp 没有实现CefRenderProcessHandler 接口,所以会缺失一部分功能。当我们需要在JavaScript中调用 C++代码时,这就完成不了了。
4 单进程启动
我们在开发的时候,如果在多进程的情况下,调试将变得比较困难,有没有办法让我们开发的时候在单进程中运行?答案是为添加启动参数。
4.1 快捷方式上添加启动参数
我们前面开发的示例,Debug目录中为可执行文件创建一个快捷方式,然后为快捷方式添加启动参数==–single-process==
4.2 VisioStudio 2019中添加启动参数
在命令参数后面添加 –single-process
再次启动后,发现任务管理器中就只有一个进程了。
5 单一执行体与分离执行体
前面我们自己编写的例子,cefsimple 和cefclient 这两个例子都使用的是单一执行体。所谓单一执行体就是启动同一个 exe文件来分别创建不同的进程。(多进程)
当以单一执行体运行时,如我们自己的例子和cefsimple ,cefclient 它们都是先执行进程,然后再初始化的。
所谓分离执行体就是启动 browser进程 的是一个exe文件,而其它进程比如render进程 执行的是另外一个exe,它是先初始化,告诉 browser进程(主进程)分离的执行体是哪个exe文件,然后再执行子进程。当使用独立的子进程执行体时,我们需要2个分开的可执行工程和入口函数。
下面就看看如何开发分离执行体的应用。
5.1 分离执行体项目
5.1.1 创建分离体项目
在原有的解决方案中再添加一个子项目,名称为QyRender,将来的分离执行体就是 QyRender.exe
。 这个项目使用 QT Console Application ,因为它不会用到图形界面
添加项目配置include目录和lib引用
告诉操作系统使用Windows的方式运行这个exe,如果不设置这一步,当这个进程启动后,会弹出控制台窗口。
5.1.2 实现CefApp,CefRenderProcessHandler
我们说创建渲染进程的时候,为CefExecuteProcess 函数传递一个 CefApp,用于将来JavaScript与C++的通信,暂时这个类中什么也不做,先放在这里
// QyAppRender.h
#include <qobject.h>
#include "include/cef_app.h"class QyAppRenderer :public CefApp, public CefRenderProcessHandler {public:QyAppRenderer();//重写CefApp 中的GetRenderProcessHandler方法CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE{return this;}//实现 CefRenderProcessHandler 接口中的方法void OnBrowserCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefDictionaryValue> extra_info) OVERRIDE;private:// Include the default reference counting implementation.IMPLEMENT_REFCOUNTING(QyAppRenderer);
};// QyAppRender.cpp
#include "QyAppRenderer.h"/// <summary>
/// 构造方法空实现
/// </summary>
QyAppRenderer::QyAppRenderer() {}
/// <summary>
/// 当CefBrowser对象已经创建的时候回调,将来JS与c++通信的时候会用到,现在只做空实现
/// </summary>
/// <param name="browser"></param>
/// <param name="extra_info"></param>
void QyAppRenderer::OnBrowserCreated(CefRefPtr<CefBrowser> browser,CefRefPtr<CefDictionaryValue> extra_info) {}
5.1.3 分离体项目main函数
分离器项目的main函数很简单,拿到命令行参数,创建 CefApp (QyAppRenderer)后执行CefExecuteProcess即可
#include "include/cef_app.h"
#include "include/cef_command_line.h"
#include "QyAppRenderer.h"
int main(int argc, char *argv[])
{HINSTANCE hInstance = GetModuleHandle(nullptr);//构造命令行CefMainArgs main_args(hInstance);// Optional implementation of the CefApp interface.// 可选择性地实现CefApp接口CefRefPtr<QyAppRenderer> app(new QyAppRenderer);// Execute the sub-process logic. This will block until the sub-process should exit.// 执行子进程逻辑,此时会堵塞直到子进程退出。return CefExecuteProcess(main_args, app.get(),nullptr);
}
5.2 主项目main函数
在主项目的 main函数中,就不需要执行 CefExecuteProcess了,只需要在 CefSettings中配置执行体exe文件路径即可。主项目启动的就是browser 进行它只需要执行初始化cef和开始消息循环就完事了
#include "mainwindow.h"
#include <QtWidgets/QApplication>
#include "include/cef_command_line.h"
#include "include/cef_sandbox_win.h"
#include "cef/simple_app.h"
#include <qdebug.h>
int main(int argc, char *argv[])
{// Enable High-DPI support on Windows 7 or newer.CefEnableHighDPISupport();// 通过GetModuleHandle 获取 HINSTANCEHINSTANCE hInstance = GetModuleHandle(nullptr);// 开启 QT 消息循环SimpleApp* cefApp=new SimpleApp;QApplication a(argc, argv);//CEF 命令行参数CefMainArgs main_args(hInstance);CefSettings settings;settings.no_sandbox = true;settings.multi_threaded_message_loop = true;//分离的执行体QString executerPath = QApplication::applicationDirPath().append("\\QyRender.exe");CefString(&settings.browser_subprocess_path).FromASCII(executerPath.toStdString().c_str());MainWindow w(cefApp, nullptr);w.show();CefRefPtr<SimpleApp> app(cefApp);// 初始化CEFCefInitialize(main_args, settings, app.get(), nullptr);int ret = a.exec();// Shut down CEF.CefShutdown();return ret;
}
这里的重点是 settings.browser_subprocess_path设置为执行体文件路径,去掉CefExecuteProcess 函数
5.3 编译整个解决方案
编译以后,会把可执行文件放入到解决方案的Debug目录中。
可以看到有两个可执行文件,现在启动 QyCefVS.exe, 查看进程:
注意分离的 QyRender虽然名字有个render,但是它是可以启动除了 browser 进程以外所有进程的。
代码请访问 GitHub qt_cef_07分支
QT集成CEF07-理解CEF多进程相关推荐
- QT集成QML和JavaScript
QT 集成QML和JavaScript 集成QML和JavaScript JavaScript表达式 JavaScript资源 JavaScript导入 JavaScript主机环境 精调JavaSc ...
- QT之深入理解QThread
QT之深入理解QThread 理解QThread之前需要了解下QThread类,QThread拥有的资源如下(摘录于QT 5.1 帮助文档): 在以上资源中,本文重点关注槽:start():信号:st ...
- 旺谷图控与QT集成开发
1.与QT集成开发 1.1旺谷图控QT开发包 QT开发包中包含:头文件.静态库.动态库.元件库.vgs工程或元件组成.其中元件库和vgs工程由vgs开发工具发布,拷贝到Qt工程的debug和relea ...
- Qt 集成 FFmpeg 实现颜色格式转换
目录 1. Qt 集成 FFmpeg 1.1 下载 FFmpeg 1.2 Qt 集成 FFmpeg 1.2.1 修改 .pro 文件 1.2.2 放入 dll 文件 1.2.3 代码中使用 FFmpe ...
- Qt 集成miniblink浏览器库之1编译使用
1.miniblink简介 miniblink是一款精简小巧的浏览器控件,由龙泉寺扫地僧基于chromium精简而成,是市面上最小巧的chromium内核控件没有之一. 它仅10余M大小,只需一个dl ...
- 【机器视觉】Qt集成Halcon开发环境详解(二)
00. 目录 文章目录 00. 目录 01. 概述 02. Halcon动态库配置方案一 03. Halcon动态库配置方案二 04. 问题讨论 05. 附录 01. 概述 Qt中集成Halcon开发 ...
- 个人对持续集成的理解和实践
目前团队人数很少,也没有真正意义上的测试人员,那么如何保证代码质量呢?最近看了<持续交付--发布可靠软件的系统方法>很受启发,突然也想通了很多集成开发工具的设计理念,并做了一些实践,特别记 ...
- Qt 集成miniblink浏览器库之4 解决兼容性问题
之前介绍了如何miniblink集成到qt,采用wkeCreateWebWindow来创建一个浏览器窗口,wkeCreateWebWindow有三种方式 typedef enum _wkeWindow ...
- Qt+腾讯IM开发笔记(一):腾讯IM介绍、使用和Qt集成腾讯IM-SDK的工程模板Demo
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/119305601 长期持续带来更多项目与技术分享,咨询请 ...
最新文章
- golang 绘图库_golang在图片上绘制中文不乱码的方法
- 大脑模拟AI学习策略,这项逼近反向传播的研究登上《自然-神经科学》
- 中的挂起是什么意思_数字博物馆是什么意思?数字博物馆用到了哪些技术?
- redis 4.0.8 源码包安装集群
- php键名改为0.1.2.3,揭秘 0.1 + 0.2 != 0.3(php 请自觉点用round)
- 如何看待程序员内卷现象
- 11)PHP,单选框和复选框的post提交方式处理
- java 人脸识别 demo_java引用Arcface,实现人脸识别(demo)
- DataNode,NameNode,JobTracker,TaskTracker用jps查看无法启动解决办法
- MaxCompute - ODPS重装上阵 第一弹 - 善用MaxCompute编译器的错误和警告
- python面试自我介绍_如何拿到半数面试公司Offer——我的Python求职之路
- 前端小白程序员入门之前知道这些,半年后都拿到8K+的offer
- WinHEC 2008 China
- 怎么快速将Excel文件转为DBF格式文件
- html yy直播,网页YY直播间进入方法 网页YY迷你版怎么用
- 阿里云(飞天)里的 盘古
- 常用统计量及其常见分布
- CentOS虚拟机根分区磁盘扩容操作
- 使用 C# 进行 Outlook 2007 编程
- 中国电信数字中南智慧网自动登录油猴脚本