CEF3专辑-基础篇-03-代码初试1
本帖最后由 KenTien 于 2025-2-17 14:59 编辑3. 代码初试3.1. 以cefclient实例开始3.1.1. mian函数入口我们打开Cmake后的VS文件,运行cef.sln,在cefclient项目中找到cefclient_win.cc文件双击打开
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
//直接RunMain接管
return client::RunMain(hInstance, nCmdShow);
}
//我们来到namespace client
namespace client {
namespace {
int RunMain(HINSTANCE hInstance, int nCmdShow) {
/*启用高分屏支持,如果不调用,你的应用程序在一些高分辨率的屏幕下将显示得很模糊*/
CefEnableHighDPISupport();
/*为CEF提供命令行参数*/
CefMainArgs main_args(hInstance);
void* sandbox_info = nullptr;
#if defined(CEF_USE_SANDBOX)
/*管理沙箱信息对象的生命周期。这对于Windows上的沙盒支持是必要的。详细信息请参见cef_sandbox_win.h*/
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
/*解析此方法中使用的命令行参数一般为初始传进来的参数*/
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
CefRefPtr<CefApp> app;
//CefApp代表的是一个进程的抽象。通过一个工具函数GetProcessType从命令行中解析--type=xxx(浏览器进程有没有这个命令参数)来判断进程的类型,然后实例化对应的CefApp子类,最后通过CefExecuteProcess来运行进程。
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();
// 如果有以上子进程则执行子进程
int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0)
return exit_code;
// 创建主上下文对象。包括url等
auto context = std::make_unique<MainContextImpl>(command_line, true);
/* CEF 的配置对象,类似日志级别、调试端口等都是通过这个对象设置的,这里我们就全部使用默认值*/
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
/*根据命令行参数填充设置*/
context->PopulateSettings(&settings);
/*创建主消息循环对象*/
std::unique_ptr<MainMessageLoop> message_loop;
if (settings.multi_threaded_message_loop)
message_loop.reset(new MainMessageLoopMultithreadedWin);
else if (settings.external_message_pump)
message_loop = MainMessageLoopExternalPump::Create();
else
message_loop.reset(new MainMessageLoopStd);
/*CEF初始化操作*/
context->Initialize(main_args, settings, app, sandbox_info);
/*注册方案处理程序,加载本地界面*/
test_runner::RegisterSchemeHandlers();
auto window_config = std::make_unique<RootWindowConfig>();
window_config->always_on_top =
command_line->HasSwitch(switches::kAlwaysOnTop);
window_config->with_controls =
!command_line->HasSwitch(switches::kHideControls);
window_config->with_osr =
settings.windowless_rendering_enabled ? true : false;
/*创建第一个窗口*/
context->GetRootWindowManager()->CreateRootWindow(std::move(window_config));
/*运行消息循环。这将阻塞,直到所有窗口被销毁后RootWindowManager调用Quit()*/
int result = message_loop->Run();
/*关闭CEF*/
context->Shutdown();
/*以创建对象的相反顺序释放对象*/
message_loop.reset();
context.reset();
return result;}}}
➢ CEF 框架如何启动多个进程CefExecuteProcess 负责启动子进程。一般我们启动一个 CEF 应用,你会发现任务管理器里有好几个进程。分别包括:● 浏览器进程(Browser)。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。● 渲染进程(Renderer)。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,一般情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。● GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。● 网络进程(NetWork)。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。● 插件进程(Plug-in)。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。这些进程中除了主进程是由用户启动的外,其他子进程都是 CEF 框架通过 CefExecuteProcess() 方法启动的。主进程启动后,执行到此方法时,此方法会立即返回 -1 。接下去主进程就会进入 CEF 的消息循环,在适当的时候主进程会以特殊的命令行参数,多次启动你的可执行文件,这样就创建了多个子进程。子进程启动后也会执行到这个 CefExecuteProcess 方法,但子进程执行此方法会被阻塞(不会继续执行后面的逻辑),当子进程执行完它们的任务后,这个方法将返回一个大于等于 0 的值。后面的代码就不会执行。CefExecuteProcess 方法的第一个参数就是我们前面介绍的 CefMainArgs 对象,第二个参数可以是不同进程的业务处理对象,也可以为空。基于一切从简的原则,我们这里传递了一个空指针。第三个参数与 Chromium 沙箱有关,此处我们也没有设置。
➢ CEF 特有的智能指针CefRefPtr 是一个智能指针类型,它内部有一个引用计数,持有这个指针的使用者越多,计数就越高,使用完了之后,计数也会相应地减少;当没有任何使用者之后,指针就会自动释放它指向的对象。这与现代 C++ 中的智能指针并没有什么明显区别,CEF 框架大量使用了类似的智能指针,减轻了开发者的心智负担。➢ CEF 框架初始化及消息循环接下来主进程会执行 CefInitialize 方法,这个方法负责初始化 CEF 的浏览器进程处理类(注意:后文我们提到的浏览器进程与前文提到的主进程属于同一个进程)。这个方法的第一个参数仍然是我们前面创建的 CefMainArgs 对象,第二个参数是 CefSettings 对象,第三个参数就是 App 对象的指针,这里是通过 CefRefPtr 智能指针的 get 方法获取的。第四个参数与沙箱有关,这里我们依然置空。CefRunMessageLoop 负责开启 CEF 消息循环,这个方法会阻塞后面代码的执行,一直到应用程序的某个地方调用了 CefQuitMessageLoop 方法之后,这个方法才会退出执行。(CefQuitMessageLoop 方法会发射应用程序退出的消息,CefRunMessageLoop 方法会收到这个消息,收到这个消息后就退出方法了。)CefShutdown 方法会结束主进程,释放资源。最后应用程序退出。3.1.2. 浏览器子进程的入口
//以browser process为例:
//浏览器进程的客户端应用程序实现
class ClientAppBrowser : public ClientApp, public CefBrowserProcessHandler {
public:
/*浏览器委托的接口。所有的delegate必须通过CreateDelegates返回。不要执行Delegate构造函数中的工作。有关文档,请参阅CefBrowserProcessHandler*/
class Delegate : public virtual CefBaseRefCounted {
public:
//即将处理命令行
virtual void OnBeforeCommandLineProcessing(CefRefPtr<ClientAppBrowser> app,
CefRefPtr<CefCommandLine> command_line) {}
//CEF浏览器的settings对象中注册自定义的偏好设置。
virtual void OnRegisterCustomPreferences(CefRefPtr<ClientAppBrowser> app,
cef_preferences_type_t type,CefRawPtr<CefPreferenceRegistrar> registrar) {}
//浏览器上下文初始化完成后执行一些操作
virtual void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) {}
//即将启动子进程
virtual void OnBeforeChildProcessLaunch(CefRefPtr<ClientAppBrowser> app,
CefRefPtr<CefCommandLine> command_line) {}};
typedef std::set<CefRefPtr<Delegate>> DelegateSet;
ClientAppBrowser();
/*调用填充|设置|基于|命令行|和其他全局状态*/
static void PopulateSettings(CefRefPtr<CefCommandLine> command_line,
CefSettings& settings);
private:
/*注册可cookie的方案。由cefclient在client_app_delegates_browser.cc中实现*/
static void RegisterCookieableSchemes(std::vector<std::string>& cookieable_schemes);
/*创建所有的Delegate对象。由cefclient在client_app_delegates_browser.cc中实现*/
static void CreateDelegates(DelegateSet& delegates);
// CefApp方法
void OnBeforeCommandLineProcessing(const CefString& process_type,
CefRefPtr<CefCommandLine> command_line) override;
CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
return this;}
// CefBrowserProcessHandler 方法
void OnRegisterCustomPreferences(cef_preferences_type_t type,
CefRawPtr<CefPreferenceRegistrar> registrar) override;
void OnContextInitialized() override;
void OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line) override;
void OnScheduleMessagePumpWork(int64 delay) override;
// Set of supported Delegates.
DelegateSet delegates_;
IMPLEMENT_REFCOUNTING(ClientAppBrowser);
DISALLOW_COPY_AND_ASSIGN(ClientAppBrowser);
};
➢ 浏览器进程的行为ClientAppBrowser 类继承自 ClientApp 和 CefBrowserProcessHandler 类,这两个基类提供了对浏览器进程的行为描述,比如:OnContextInitialized(浏览器进程的渲染线程初始化成功后被调用)、OnBeforeCommandLineProcessing (命令行参数被 CEF 和 Chromium 处理之前触发)等。我们在 ClientAppBrowser 类里介绍几个重写的基类方法。GetBrowserProcessHandler 方法返回浏览器进程的处理类实例指针,这里我们返回了 App 类的实例指针自身(也就是它自己)。浏览器进程内可能会有多个线程在执行,任何一个线程都有可能调用这个方法。OnContextInitialized 方法在浏览器进程的主线程初始化成功后被调用,代表着浏览器进程已经初始化成功了。
页:
[1]