FBrowserCEF3Lib浏览器开发技术交流网址

 找回密码
 立即注册
查看: 2577|回复: 0

CEF3专辑-基础篇-03-代码初试1

[复制链接]

签到天数: 32 天

[LV.5]常住居民I

10

主题

34

回帖

554

积分

中级会员LV1

太阳月亮月亮星星

积分
554
发表于 2025-2-17 14:36:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 KenTien 于 2025-2-17 14:59 编辑

3. 代码初试3.1. 以cefclient实例开始3.1.1. mian函数入口
我们打开Cmake后的VS文件,运行cef.sln,在cefclient项目中找到cefclient_win.cc文件双击打开

[C++] 查看源码 复制代码
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. 浏览器子进程的入口
[C++] 查看源码 复制代码
//以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 方法在浏览器进程的主线程初始化成功后被调用,代表着浏览器进程已经初始化成功了。



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们 在线手册 视频教程 赞助支持 CEF官网 易语言官网 火山开发官网
拒绝任何人以任何形式在本网站发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表本网站立场!

网址帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!

Archiver| 手机版| 小黑屋| 联系QQ:283987887 劰载中...

Powered by Discuz! X5.0 GMT+8, 2026-9-17 07:04 , Processed in 0.023626 second(s), 11 queries , Redis On.

 川公网安备51090302000262 蜀ICP备2023034329号
快速回复 返回顶部 返回列表