CEF2 -- 基于CEF的多进程浏览器

如何构建CEF多进程模式

上一篇介绍如何在win32项目中引入CEF。这一篇介绍一下,如何管理CEF的多进程模式,并进行调试。

CEF是一个浏览器控件,进一步说是多组接口集合。嵌入CEF就是实现基于接口的二次开发。但这些接口又比较特殊,因为是开源的,所以可以跟踪、调试、甚至根据自己的需要进行定制、修改。

接口就不做介绍了。下面就从项目由浅入深引入的CEF模块,分块做一下介绍。

  • CEF的多进程模式

如果把CEF结构大致分一下,会分出三大块来,browser、render、其他。CEF也支持单进程模式,可以通过设置信息修改。默认多进程模式。其中browser模块主要处理UI部分、Render部分主要做js解释运行、DOM结构解析,其他模块非必须,比如GPU加速渲染、第三方插件等。

  • 管理多个进程

这里找不到一个合适的词,只能暂时用管理,其实不是管理,更贴切说是多进程的入口创建。先看一下CEF源码提供的demo。cefsimple实现了browser的接口,render使用默认行为

// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPTSTR lpCmdLine,
                      int nCmdShow) {
  UNREFERENCED_PARAMETER(hPrevInstance);
  UNREFERENCED_PARAMETER(lpCmdLine);

  // Enable High-DPI support on Windows 7 or newer.
  CefEnableHighDPISupport();

  void* sandbox_info = NULL;

#if defined(CEF_USE_SANDBOX)
  // Manage the life span of the sandbox information object. This is necessary
  // for sandbox support on Windows. See cef_sandbox_win.h for complete details.
  CefScopedSandboxInfo scoped_sandbox;
  sandbox_info = scoped_sandbox.sandbox_info();
#endif

  // Provide CEF with command-line arguments.
  CefMainArgs main_args(hInstance);

  // CEF applications have multiple sub-processes (render, plugin, GPU, etc)
  // that share the same executable. This function checks the command-line and,
  // if this is a sub-process, executes the appropriate logic.
  int exit_code = CefExecuteProcess(main_args, NULL, sandbox_info);
  if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
  }

  // Specify CEF global settings here.
  CefSettings settings;

#if !defined(CEF_USE_SANDBOX)
  settings.no_sandbox = true;
#endif

  // SimpleApp implements application-level callbacks for the browser process.
  // It will create the first browser instance in OnContextInitialized() after
  // CEF has initialized.
  CefRefPtr<SimpleApp> app(new SimpleApp);

  // Initialize CEF.
  CefInitialize(main_args, settings, app.get(), sandbox_info);

  // Run the CEF message loop. This will block until CefQuitMessageLoop() is
  // called.
  CefRunMessageLoop();

  // Shut down CEF.
  CefShutdown();

  return 0;
}

再看一下cefclient,实现自己对browser和render行为管理

namespace client {
namespace {

int RunMain(HINSTANCE hInstance, int nCmdShow) {
  // Enable High-DPI support on Windows 7 or newer.
  CefEnableHighDPISupport();

  CefMainArgs main_args(hInstance);

  void* sandbox_info = NULL;

#if defined(CEF_USE_SANDBOX)
  // Manage the life span of the sandbox information object. This is necessary
  // for sandbox support on Windows. See cef_sandbox_win.h for complete details.
  CefScopedSandboxInfo scoped_sandbox;
  sandbox_info = scoped_sandbox.sandbox_info();
#endif

  // Parse command-line arguments.
  CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
  command_line->InitFromString(::GetCommandLineW());

  // 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;

  // Create the main context object.
  scoped_ptr<MainContextImpl> context(new MainContextImpl(command_line, true));

  CefSettings settings;

#if !defined(CEF_USE_SANDBOX)
  settings.no_sandbox = true;
#endif

  // Populate the settings based on command line arguments.
  context->PopulateSettings(&settings);

  // Create the main message loop object.
  scoped_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);

  // Initialize CEF.
  context->Initialize(main_args, settings, app, sandbox_info);

  // Register scheme handlers.
  test_runner::RegisterSchemeHandlers();

  RootWindowConfig window_config;
  window_config.with_controls =
      !command_line->HasSwitch(switches::kHideControls);
  window_config.with_osr = settings.windowless_rendering_enabled ? true : false;

  // Create the first window.
  context->GetRootWindowManager()->CreateRootWindow(window_config);

  // Run the message loop. This will block until Quit() is called by the
  // RootWindowManager after all windows have been destroyed.
  int result = message_loop->Run();

  // Shut down CEF.
  context->Shutdown();

  // Release objects in reverse order of creation.
  message_loop.reset();
  context.reset();

  return result;
}

}  // namespace
}  // namespace client

// Program entry point function.
int APIENTRY wWinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPTSTR lpCmdLine,
                      int nCmdShow) {
  UNREFERENCED_PARAMETER(hPrevInstance);
  UNREFERENCED_PARAMETER(lpCmdLine);
  return client::RunMain(hInstance, nCmdShow);
}

最主要的区别在这里

// 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();

 默认都是多进程模式。下面的这种方式可以直接把进程attach到当前进程中就可以进行调试了。如果使用第一种方式创建进程,render等其他进程使用默认行为,调试的时候需要下载对应版本的cef.pdb。

  • 单进程模式

可以通过参数进行设置,我试着使用了单进程,但一直无法调试,可能需要额外的pdb信息,时间紧迫也没时间做更多尝试。浏览器设置信息如下

    // Specify CEF global settings here.
    CefSettings settings;

#if !defined(CEF_USE_SANDBOX)
    settings.no_sandbox = true;
#endif

    CefSettingsTraits::init(&settings);
    //这个设置能实现单进程运行Cef浏览器插件,我们实际应用中采用的是双进程模式,即一个主进程,一个render渲染进程  
    //settings.single_process = true;
    //settings.no_sandbox = true;  

    //设置渲染进程的名称,因为在相同目录下,没有指定路径  
    //CefString(&settings.browser_subprocess_path).FromWString(L"Render.exe");

    //禁用Cef的消息循环,采用DuiLib的消息循环  
    //settings.multi_threaded_message_loop = true;

    //设置本地语言  
    //CefString(&settings.locale).FromWString(L"zh-CN");

    //缓存数据路径  
    //CefString(&settings.cache_path).FromWString(cache_path);

    //debug日志路径  
    //CefString(&settings.log_file).FromWString(log_file);

    // Initialize CEF.
    CefInitialize(main_args, settings, app.get(), sandbox_info);

以上信息是项目进行之余,临时做的记录和摘要,比较粗糙,希望还有时间完善。 

猜你喜欢

转载自blog.csdn.net/moyebaobei1/article/details/81388427
CEF