当前位置: 首页 > news >正文

CEF之多进程调试

CEF3 使用多进程。其中包括:一个浏览器进程、一个渲染进程和若干其他进程(处理插件的进程、处理GUP的进程等)。

  • 浏览器进程处理窗口的创建和绘制,以及网络访问等,浏览器进程包含了应用程序的主要逻辑
  • 渲染进程负责渲染 HTML 以及执行 JavaScript ,访问 DOM 等
  • 其他进程则进行插件处理,GPU 处理等(如果有的话)。
  • 多个进程之间通过 IPC (Inter-Process Communication) 通信

首先启动浏览器进程(Browser),然后启动渲染进程(Render),再启动其他进程。

默认情况下 CEF3 使用一个 exe 的多个实例来实现上述的多个进程。比如在 CEF3:用CEF3实现最简单的浏览器 中,在 WinMain 开始时会执行下面的代码:

    // 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, NULL);
    if (exit_code >= 0)
    {
        // The sub-process has completed so return here.
        return exit_code;
    }

这里 CefExecuteProcess() 会根据不同的命令行参数来执行不同的进程如果是浏览器进程,该函数立即返回,返回值为 -1如果是其他进程,则在浏览器退出时才返回,返回值是一个大于0的数。
运行结果如下图,可以看到同一个 exe 运行了三个实例。
这里写图片描述

也可以通过设置 CefSettings.browser_subprocess_path 来以不同的 exe 实现 CEF 的多个进程。下面通过示例说明这一点。

创建两个工程,一个命名为 BrowserProcess 作为浏览器进程,另一个命名为 BrowserSubProcess 作为其他子进程。工程的设置可以参见 CEF3:用CEF3实现最简单的浏览器。

在 BrowserProcess 工程下创建 main.cpp,编写如下代码。注意这里不再调用 CefExecuteProcess(),而是设置 CefSettings.browser_subprocess_path 的值来指定子进程路径。

#include "include/cef_app.h"
#include "include/cef_browser.h"
#include "include/cef_client.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include <Windows.h>

class MyClient : public CefClient, public CefLifeSpanHandler
{
public:
    virtual ~MyClient() {}
    virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
    virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { CefQuitMessageLoop(); }
private:
    IMPLEMENT_REFCOUNTING(MyClient);
};

class MyApp : public CefApp, public CefBrowserProcessHandler
{
public:
    virtual ~MyApp() {}
    virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override { return this; }
    virtual void OnContextInitialized() override 
    {
        CEF_REQUIRE_UI_THREAD();
        CefWindowInfo window_info;
        window_info.SetAsPopup(NULL, "cefsimple");
        CefRefPtr<MyClient> client(new MyClient());
        CefString url = "http://www.baidu.com";
        CefBrowserSettings browser_settings;
        CefBrowserHost::CreateBrowser(window_info, client, url, browser_settings, NULL);
    }
private:
    IMPLEMENT_REFCOUNTING(MyApp);
};

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 
{
    CefMainArgs main_args(hInstance);
    CefSettings settings;
    settings.no_sandbox = true;
    // 获取子进程路径
    WCHAR subProcessPath[MAX_PATH] = { 0 };
    GetModuleFileNameW(hInstance, subProcessPath, MAX_PATH);
    *(wcsrchr(subProcessPath, L'\\') + 1) = L'\0';
    LPCWSTR SUB_PROCESS_NAME = L"BrowserSubProcess.exe";
    wcsncat_s(subProcessPath, MAX_PATH, SUB_PROCESS_NAME, wcslen(SUB_PROCESS_NAME));
    // 设置子进程路径
    cef_string_from_wide(subProcessPath, MAX_PATH, &settings.browser_subprocess_path);

    auto myApp = CefRefPtr<MyApp>(new MyApp());

    CefInitialize(main_args, settings, myApp.get(), NULL);
    CefRunMessageLoop();
    CefShutdown();

    return 0;
}

在 BrowserSubProcess 工程下创建 main.cpp,编写如下代码。代码相当简单,仅仅是调用了 CefExecuteProcess()。

#include "include/cef_app.h"
#include <Windows.h>

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    CefMainArgs main_args(hInstance);

    return CefExecuteProcess(main_args, NULL, NULL);
}

编译两个工程后,确保两个 exe 在同一目录下,运行,结果如下。可以看到运行了不同的 exe 。
这里写图片描述

Chrome的多进程模型给DEBUG带来了很大的挑战。

一、如果你设置代码的断点,默认情况下,VS只会跟踪那些在主进程Browser代码中的那些断点。VS提供了"Attach To Process"的方法。比如当Render Process启动之后,可以用菜单"Debug"=>"Attach To Process"选项,选择那个新产生的进程,然后在你需要跟踪的代码处设置断点,就可以。但是这种方法,只能在子进程启动之后,才比较有效,如果我们想在子进程启动时,跟踪某些代码的执行,就没有办法了。

二、针对这个Chrome从源代码级别提供了支持。共有两种方法:

1. 用启动选项“--single-process“,以单进程的方法来启动Chrome。

2.用启动选项"--renderer-startup-dialog"和"--no-sandbox"。两个选项将会让子进程在启动之后,弹出一个模态对话框。之后在关闭这个对话框之后才可以继续运行代码。在这期间,我们可以用上述"Attach To Process"的方法来跟踪子进程代码的执行。

之所以要加上"--no-sandbox",是因为默认情况下Chrome的子进程的创建和启动是在sanbox中(也就说访问系统资源是严格限制的),无法显示模态对话框UI

Render进程的主函数如下:

int RendererMain(const MainFunctionParams& parameters) {
  const CommandLine& parsed_command_line = parameters.command_line_;
  base::ScopedNSAutoreleasePool* pool = parameters.autorelease_pool_;
 
  // This function allows pausing execution using the --renderer-startup-dialog
  // flag allowing us to attach a debugger.
  // Do not move this function down since that would mean we can't easily debug
  // whatever occurs before it.
  HandleRendererErrorTestParameters(parsed_command_line);

HandleRenderErrorTestParameters函数会显示这个模态对话框。

// This function provides some ways to test crash and assertion handling
// behavior of the renderer.
static void HandleRendererErrorTestParameters(const CommandLine& command_line) {
  // This parameter causes an assertion.
  if (command_line.HasSwitch(switches::kRendererAssertTest)) {
    DCHECK(false);
  }
 
  // This parameter causes a null pointer crash (crash reporter trigger).
  if (command_line.HasSwitch(switches::kRendererCrashTest)) {
    int* bad_pointer = NULL;
    *bad_pointer = 0;
  }
 
  if (command_line.HasSwitch(switches::kRendererStartupDialog)) {
#if defined(OS_WIN)
    std::wstring title = l10n_util::GetString(IDS_PRODUCT_NAME);
    title += L" renderer";  // makes attaching to process easier
    ::MessageBox(NULL, L"renderer starting...", title.c_str(),
                 MB_OK | MB_SETFOREGROUND);
#elif defined(OS_LINUX)

 

相关文章:

  • 理解WebKit和Chromium: Content API和CEF3
  • C++ explicit关键字详解
  • Chromium的启动流程
  • WMIC
  • Windows下调试工具Windbg入门
  • 符号文件
  • 浏览器沙箱(sandBox)到底是什么?
  • 渲染
  • URI和URL的区别比较与理解
  • CEF调试Render进程
  • Chrome V8引擎介绍
  • c++中的 extern “C“
  • 中断向量表
  • WM_NCCREATE与WM_CREATE的区别
  • GetWindowLongPtr
  • 08.Android之View事件问题
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Android组件 - 收藏集 - 掘金
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • spring + angular 实现导出excel
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vuex 笔记整理
  • XML已死 ?
  • 安卓应用性能调试和优化经验分享
  • 欢迎参加第二届中国游戏开发者大会
  • 检测对象或数组
  • 前端设计模式
  • 我的业余项目总结
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​你们这样子,耽误我的工作进度怎么办?
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #考研#计算机文化知识1(局域网及网络互联)
  • #图像处理
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (一)Java算法:二分查找
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .Net Core和.Net Standard直观理解
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET命名规范和开发约定
  • .NET与 java通用的3DES加密解密方法
  • ?.的用法
  • @Async注解的坑,小心
  • [ MSF使用实例 ] 利用永恒之蓝(MS17-010)漏洞导致windows靶机蓝屏并获取靶机权限
  • [.net]官方水晶报表的使用以演示下载