客户端内嵌页 基于Chromium Embedded Framework (CEF)

Chromium Embedded Framework (CEF)是个基于Google Chromium项目的开源Web browser控件,支持Windows, Linux, Mac平台。除了提供C/C++接口外,也有其他语言的移植版。–百度百科


前言:

由于我们项目的客户端比较老了,用户的电脑的默认的ie内核版本也比较低(老用户很多,环境比较复杂)。导致现在内嵌页的前端开发困难,很多现行的web标准不能使用。为了解决这个问题,打算用cef嵌入客户端,这样就不用考虑ie内核的问题了,能使用哪些web特性只要看使用的cef版本是否支持就可以了。


cef 源码可以谷歌的官方下载:cef 官方 下载
cef github 源码: github 源码下载
cef 已经编好的库: 编好的链接库(旧)
编译好的库(新)

因为自己下载源码,自己编译有点复杂,时间也比较久,所以这里先用官方给我们编好的链接库。等以后有时间了再用自己编源码的方式。

我使用的是 cef_binary_1.1180.832_windows.zip 可以用vs2005。 刚好我们客户的代码也是vs2005的。


解压编译

  1. 下载好 cef_binary_1.1180.832_windows.zip 后进行解压:
    这里写图片描述

  2. cefclient 是示例程序。 lib 里面的 libcef.lib 是已经编好的库,在客户端代码工程需要引入。libcef.lib是用c写的,所以为了c++方便使用,libcef_dell_wrapper 是c++ 对 libcef 封装, 源码在 libcef_dll目录下

  3. 用vs2005打开 cefclient2005.sln
    这里写图片描述

  4. 工程目录中,cefclient->include 和 libcef_dll_wrapper->include 是libcef.lib 库的引用头文件,就是解压根目录下的include 中的文件。我们客户端的工程中也引入这些头文件。

  5. 在编 cefclient 的时候,遇到了 atlthunk.lib 找不到的错误。弄了好久没解决,后面索性在工程属性中Linker->input 中把这个依赖库去掉。不影响编译。libcef_dll_wrapper 编译基本没遇到问题。


嵌入客户端工程


先研究下 cefclient 示例程序中的代码,不是很复杂。基本流程如下:


  // 详细的代码在 cefclient_win.cpp 
  // 这里只是一些片段
  // Program entry point function.
  int APIENTRY wWinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPTSTR    lpCmdLine,
                      int       nCmdShow) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Retrieve the current working directory.
    if (_getcwd(szWorkingDir, MAX_PATH) == NULL)
      szWorkingDir[0] = 0;

    // Parse command line arguments. The passed in values are ignored on Windows.
    // 初始化命令行参数
    AppInitCommandLine(0, NULL);

    CefSettings settings;
    CefRefPtr<CefApp> app;

    // Populate the settings based on command line arguments.
    // 内嵌页的一些配置设置,详细内容要看CefSettings类
    AppGetSettings(settings, app);

    // Initialize CEF.
    // cef 开始进行初始
    // 这里在我们自己程序运行的时候,如果根目录没有把示例程序根目录的locales拷到客户端跟目录。
    // 初始化就会崩溃,而且没有任何提示和报错(坑爹的google o(╥﹏╥)o 找了好久才发现)。
    // 具体下面的注意点会提到。
    CefInitialize(settings, app);
    ........

    // 其他自己逻辑代码.......

    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_CEFCLIENT, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization
    if (!InitInstance (hInstance, nCmdShow))
      return FALSE;

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT));

    // Register the find event message.
    uFindMsg = RegisterWindowMessage(FINDMSGSTRING);

    int result = 0;

    // settings.multi_threaded_message_loop 是 true 还是 false 的处理 会影响到
    // 关闭 CefShutdown() 函数,如果是 settings.multi_threaded_message_loop = true
    // 单处理不是 CefRunMessageLoop() , 在准备退出并调用 CefShutdown 就会崩溃。
    if (!settings.multi_threaded_message_loop) {
      // Run the CEF message loop. This function will block until the application
      // recieves a WM_QUIT message.
      CefRunMessageLoop();
    } else {
      MSG msg;

      // Run the application message loop.
      while (GetMessage(&msg, NULL, 0, 0)) {
        // Allow processing of find dialog messages.
        if (hFindDlg && IsDialogMessage(hFindDlg, &msg))
          continue;

        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
        }
      }

      result = static_cast<int>(msg.wParam);
    }

    // Shut down CEF.
    / cefShutdown() 崩溃下面会提到。
    CefShutdown();

    return result;
  }

    // 创建内嵌页
    // 详细的代码在 cefclient_win.cpp 
    // Create the single static handler class instance
  g_handler = new ClientHandler();
  g_handler->SetMainHwnd(hWnd);

  // Create the child windows used for navigation
  RECT rect;
  GetClientRect(hWnd, &rect);

  CefWindowInfo info;
  CefBrowserSettings settings;

  // Populate the settings based on command line arguments.
  AppGetBrowserSettings(settings);

  // Initialize window info to the defaults for a child window
  info.SetAsChild(hWnd, rect);

  // Creat the new child browser window
  int nRet = CefBrowser::CreateBrowser(info,
  static_cast<CefRefPtr<CefClient> >(g_handler),
  g_handler->GetStartupURL(), settings);

根据自己项目的不同,不一定要和示例程序完全一样,不过核心的步骤一定要有:

  // Parse command line arguments. The passed in values are ignored on Windows.
  AppInitCommandLine(0, NULL);

  CefSettings settings;
  CefRefPtr<CefApp> app;

  // Populate the settings based on command line arguments.
  AppGetSettings(settings, app);
// 初始化
  // Initialize CEF.
  CefInitialize(settings, app);

  g_handler = new ClientHandler();
  g_handler->SetMainHwnd(hWnd);

// 创建
  int nRet = CefBrowser::CreateBrowser(info,
          static_cast<CefRefPtr<CefClient> >(g_handler),
          g_handler->GetStartupURL(), settings);
// 关闭
  // Shut down CEF.
  CefShutdown();

客户端工程引入

  1. 拷贝解压根目录中的 include 目录到客户端工程目录, 并配置好引入目录(属性->c/c++ ->General ->Additionnal Include Directionories)。
  2. 将解压目录 lib->[Debug|Release] ->libcef.lib , 根目录 [Debug|Release] -> lib -> libcef_dll_wrapper.lib 两个链接库放到客户端工程链接库目录,并配置好引入目录和库配置(属性-> Linker -> Additional Library Directories 和 属性 -> Linker -> input -> Additional Dependencies)。
  3. 参照示例程序写好内嵌页后就可以编译客户端了, 编译过程中可能会出现一些问题, 下面是我遇到的。

    -4503 各种 这些类似的警告和报错。参照示例 cefclient 的 project 属性 -> c/c++ -> Advanced -> Disable Specific Warnings 里面的配置,拷贝到客户端的对应配置。可能还会有一些其他的报错,可以google 下自己找。

    -还有一些 error C2220: warning treated as error - no ‘object’ file generated 类似的错误。有可能是Runtime Library 配置的问题。例如我们客户的: 属性 -> c/c++ -> Coode Generation -> Runtime Library 的配置是 Multi-threaded Debug (/MTd), 但 libcef_dll_wrapper 中的配置是Multi-threaded Debug DLL (/MDd)。 把libcef_dll_wrapper 中的配置改成和客户端的一样然后重新编译 libcef_dll_wrapper .lib 。

    -可能还会有其他错误。可以对比客户端和示例项目cefclient的工程配置有哪些不一样的进行查验。比如cefclient中的预定义,有几个可能客户端中没有进行定义。


注意点:

  • 客户端编好后,需要把cef_binary_1.1180.832_windows.zip解压后根目录下 Debug或 Release 目录下 libGLESv2.dll , libEGL.dll, libcef.dll, icudt.dll 和 locales 目录都拷到客户端程序的根目录, 如果如果不放心就把所有的文件都拷过去。 我一开始没拷locales, 导致了程序在执行初始化CefInitialize(settings, app); 的时候就崩溃。 然后不是源码编译libcef.lib不能进行DUBG调试,找网上找了好久。

  • setting.multi_threaded_message_loop是true 还是 false。要根据自己的程序而定。我们的客户端是要配置成true的。所以不需要调用CefRunMessageLoop()处理消息。如果配置setting.multi_threaded_loop 不对,就有可能在调用CefgShutdown()的时候崩溃。


就先写到这里吧,有其他问题,或自己通过源码编译libcef.lib以后再研究。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页