小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

PC游戲中用CEF3制作內(nèi)嵌瀏覽器

 獵狐肥 2019-12-23

因?yàn)轫?xiàng)目需要,需要將游戲手機(jī)助手中的朋友圈給移植到PC游戲中,而以前游戲中的內(nèi)嵌瀏覽器采用的是IE6內(nèi)核,滿足不了我們的需求,于是決定把Cef3內(nèi)嵌到游戲中,在完成正常工作之余,利用閑散時(shí)間不斷地查找各種資料,初步完成了項(xiàng)目需求,決定寫(xiě)一篇文章來(lái)記錄我碰到的各種坑。

一開(kāi)始我只是在網(wǎng)上找了一個(gè)最后支持XP系統(tǒng)的版本–2623版本,利用這個(gè)版本進(jìn)行開(kāi)發(fā),后來(lái)發(fā)現(xiàn)不支持MP3、MP4,是需要重新編譯一個(gè)支持MP3、MP4的版本的,在網(wǎng)上找一個(gè)已編譯好了的版本(在Windows下編譯Cef3.2623并加入mp3、mp4支持(附帶源碼包和最終DLL)),最后把一些dll、資源等替換下就行,并不需要更改工程代碼,非常感謝Redrain!附上鏈接:http://blog.csdn.net/zhuhongshu/article/details/54193842

一、首先來(lái)了解下Cef3

CEF全稱Chromium Embedded Framework,是一個(gè)基于Google Chromium 的開(kāi)源項(xiàng)目。CEF的典型應(yīng)用場(chǎng)景包括:

嵌入一個(gè)兼容HTML5的瀏覽器控件到一個(gè)已經(jīng)存在的本地應(yīng)用。

· 創(chuàng)建一個(gè)輕量化的殼瀏覽器,用以托管主要用Web技術(shù)開(kāi)發(fā)的應(yīng)用。

· 有些應(yīng)用有獨(dú)立的繪制框架,使用CEF對(duì)Web內(nèi)容做離線渲染。

· 使用CEF做自動(dòng)化Web測(cè)試。

CEF3是基于Chomuim Content API多進(jìn)程構(gòu)架的下一代CEF,擁有下列優(yōu)勢(shì):

· 改進(jìn)的性能和穩(wěn)定性(JavaScript和插件在一個(gè)獨(dú)立的進(jìn)程內(nèi)執(zhí)行)。

· 支持Retina顯示器。

· 支持WebGL和3D CSS的GPU加速。

· 類似WebRTC和語(yǔ)音輸入這樣的前衛(wèi)特性。

· 通過(guò)DevTools遠(yuǎn)程調(diào)試協(xié)議以及ChromeDriver2提供更好的自動(dòng)化UI測(cè)試。

· 更快獲得當(dāng)前以及未來(lái)的Web特性和標(biāo)準(zhǔn)的能力。

翻譯自https:///chromiumembedded/cef/wiki/GeneralUsage.md

看不懂英文的可以看這篇GeneralUsage翻譯文:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF General Usage-zh-cn.md#important-concepts

二、二進(jìn)制文件及資源文件說(shuō)明

在這里插入圖片描述
有些文件是必須的,否則無(wú)法運(yùn)行

  • CEF core library.
    libcef.dll(Cef核心文件,含js引擎、網(wǎng)頁(yè)加載渲染邏輯等)

  • Crash reporting library.
    chrome_elf.dll(我還沒(méi)用上)

  • Unicode support data.
    icudtl.dat(支持Unicode需要)

  • V8 snapshot data.
    natives_blob.bin
    snapshot_blob.bin
    (V8引擎快照數(shù)據(jù)需要)

有些是根據(jù)情況可刪減的,cef可以繼續(xù)運(yùn)行,或缺失部分功能:

  • Localized resources.
    Locale file loading can be disabled completely using
    CefSettings.pack_loading_disabled. The locales directory path can be
    customized using CefSettings.locales_dir_path. (可設(shè)置本地資源的路徑)

    • locales/
      Directory containing localized resources used by CEF, Chromium and Blink. A
      .pak file is loaded from this directory based on the CefSettings.locale
      value. Only configured locales need to be distributed. If no locale is
      configured the default locale of “en-US” will be used. Without these files
      arbitrary Web components may display incorrectly.(CEF,Chromium和Blink使用的本地化資源。如果沒(méi)有區(qū)域設(shè)置將配置默認(rèn)語(yǔ)言環(huán)境“en-US”。如果沒(méi)有這些文件任意Web組件可能顯示不正確。)

  • Other resources.
    Pack file loading can be disabled completely using
    CefSettings.pack_loading_disabled. The resources directory path can be
    customized using CefSettings.resources_dir_path.

    • cef.pak

    • cef_100_percent.pak

    • cef_200_percent.pak
      These files contain non-localized resources used by CEF, Chromium and Blink.
      Without these files arbitrary Web components may display incorrectly.(這些文件包含了供CEF使用的區(qū)域無(wú)關(guān)資源,缺少這些文件任意Web組件可能顯示不正確。)

    • cef_extensions.pak
      This file contains non-localized resources required for extension loading.
      Pass the --disable-extensionscommand-line flag to disable use of this
      file. Without this file components that depend on the extension system,
      such as the PDF viewer, will not function.(此文件包含擴(kuò)展加載所需的非本地化資源傳遞--disable-extensions命令行標(biāo)志來(lái)禁止使用文件。沒(méi)有這個(gè)文件,依賴于擴(kuò)展系統(tǒng)的組件將不起作用,如PDF查看器。)

    • devtools_resources.pak
      This file contains non-localized resources required for Chrome Developer
      Tools. Without this file Chrome Developer Tools will not function.( 此文件包含Chrome開(kāi)發(fā)者工具所需的非本地化資源,缺少這個(gè)文件,Chrome開(kāi)發(fā)者工具將無(wú)法運(yùn)行。)

  • Angle and Direct3D support.

    • d3dcompiler_43.dll(required for Windows XP)

    • d3dcompiler_47.dll(required for Windows Vista and newer)

    • libEGL.dll

    • libGLESv2.dll
      Without these files HTML5 accelerated content like 2D canvas, 3D CSS and WebGL
      will not function.(Direct3D支持文件,如果缺少這些文件,HTML5在渲染2D畫(huà)布,3D CSS,WebGL時(shí)將不起作用。)

  • Widevine CDM support.

    • widevinecdmadapter.dll(DRM數(shù)字版權(quán)管理功能Widevine需要)
      Without this file playback of Widevine projected content will not function.
      See the CefRegisterWidevineCdm() function in cef_web_plugin.h for usage.

      附上英文鏈接:https:///chromiumembedded/cef/src/816f700d3ea42bedc5ca5a2314c27b761b69abc5/tools/distrib/win/README.redistrib.txt?at=master&fileviewer=file-view-default

三、引入到工程中

因?yàn)槭莾?nèi)嵌在我們自己的項(xiàng)目中,所以只需要給工程中引入cef3,將一些dll等資源文件加入到工程exe所在目錄下,在我們自己的第三方庫(kù)文件夾中加入了cef3文件夾,里面加入cef的include文件夾,和lib文件夾,lib文件夾里分別放入的是debug版本和release版本的libcef.lib文件和libcef_dll_wrapper.lib文件。
配置項(xiàng)目屬性:
C/C++ ->常規(guī)附加包含目錄中加入cef的include目錄所在路徑
鏈接器->輸入添加附加依賴項(xiàng)libcef.lib和libcef_dll_wrapper.lib
利用CMAKE創(chuàng)建cef3的demo工程,找到cefsimole工程,改造cefsimple工程的代碼,是滿足我們的需求。當(dāng)然你的需求很復(fù)雜,還是建議看看cefclient工程。cefclient是一個(gè)完整的CEF客戶端應(yīng)用程序示例,并且它的源碼包含在CEF每個(gè)二進(jìn)制發(fā)布包中。使用CEF創(chuàng)建一個(gè)新的應(yīng)用程序,最簡(jiǎn)單的方法是先從cefclient應(yīng)用程序開(kāi)始,刪除你不需要的部分。本文檔中許多示例都是來(lái)源于cefclient應(yīng)用程序。

在這里插入圖片描述
在我們?cè)陂_(kāi)發(fā)之前,需要知道一些cef的基礎(chǔ)概念。如果你和我一樣是一個(gè)新手,建議還是先了解了解基礎(chǔ)知識(shí)。

四、Cef基礎(chǔ)概念

1、多進(jìn)程架構(gòu)
首先你要知道cef3是多進(jìn)程架構(gòu)的,這點(diǎn)非常重要。Browser被定義為主進(jìn)程,負(fù)責(zé)窗口管理,界面繪制和網(wǎng)絡(luò)交互Blink的渲染和Js的執(zhí)行被放在一個(gè)獨(dú)立的Render 進(jìn)程中;除此之外,Render進(jìn)程還負(fù)責(zé)Js Binding和對(duì)Dom節(jié)點(diǎn)的訪問(wèn)。 默認(rèn)的進(jìn)程模型中,會(huì)為每個(gè)標(biāo)簽頁(yè)創(chuàng)建一個(gè)新的Render進(jìn)程。其他進(jìn)程按需創(chuàng)建,例如管理插件的進(jìn)程以及處理合成加速的進(jìn)程等都是按需創(chuàng)建。
默認(rèn)情況下,主應(yīng)用程序會(huì)被多次啟動(dòng)運(yùn)行各自獨(dú)立的進(jìn)程。這是通過(guò)傳遞不同的命令行參數(shù)給CefExecuteProcess函數(shù)做到的。如果主應(yīng)用程序很大,加載時(shí)間比較長(zhǎng),或者不能在非瀏覽器進(jìn)程里使用,則宿主程序可使用獨(dú)立的可執(zhí)行文件去運(yùn)行這些進(jìn)程。這可以通過(guò)配置CefSettings.browser_subprocess_path變量做到。
CEF3的進(jìn)程之間可以通過(guò)IPC進(jìn)行通信。Browser和Render進(jìn)程可以通過(guò)發(fā)送異步消息進(jìn)行雙向通信。甚至在Render進(jìn)程可以注冊(cè)在Browser進(jìn)程響應(yīng)的異步JavaScript API。
通過(guò)設(shè)置命令行的–single-process,CEF3就可以支持用于調(diào)試目的的單進(jìn)程運(yùn)行模型。
2、多線程
在CEF3中,每個(gè)進(jìn)程都會(huì)運(yùn)行多個(gè)線程。完整的線程類型表請(qǐng)參照cef_thread_id_t。例如,在Browser進(jìn)程中包含如下主要的線程:
TID_UI線程是瀏覽器的主線程。如果應(yīng)用程序在調(diào)用調(diào)用CefInitialize()時(shí),傳遞CefSettings.multi_threaded_message_loop=false,這個(gè)線程也是應(yīng)用程序的主線程。
TID_IO線程主要負(fù)責(zé)處理IPC消息以及網(wǎng)絡(luò)通信。
TID_FILE線程負(fù)責(zé)與文件系統(tǒng)交互。
由于CEF采用多線程架構(gòu),有必要使用鎖和閉包來(lái)保證數(shù)據(jù)的線程安全語(yǔ)義。IMPLEMENT_LOCKING定義提供了Lock()和Unlock()方法以及AutoLock對(duì)象來(lái)保證不同代碼塊同步訪問(wèn)數(shù)據(jù)。CefPostTask函數(shù)組支持簡(jiǎn)易的線程間異步消息傳遞。
可以通過(guò)CefCurrentlyOn()方法判斷當(dāng)前所在的線程環(huán)境,cefclient工程使用下面的定義來(lái)確保方法在期望的線程中被執(zhí)行。

#define REQUIRE_UI_THREAD()   ASSERT(CefCurrentlyOn(TID_UI));
#define REQUIRE_IO_THREAD()   ASSERT(CefCurrentlyOn(TID_IO));
#define REQUIRE_FILE_THREAD() ASSERT(CefCurrentlyOn(TID_FILE));

3、字符串
在C++中,通常使用CefString類來(lái)管理CEF的字符串。CefString支持與std::string(UTF8)、std::wstring(wide)類型的相互轉(zhuǎn)換。也可以用來(lái)包裹一個(gè)cef_string_t結(jié)構(gòu)來(lái)對(duì)其進(jìn)行賦值。
和std::string的相互轉(zhuǎn)換:

std::string str = “Some UTF8 string”;
// Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will occur if necessary.
str = cef_str;
str = cef_str.ToString();

和std::wstring的相互轉(zhuǎn)換:
std::wstring str = “Some wide string”;

// Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will occur if necessary.
str = cef_str;
str = cef_str.ToWString();

4、入口函數(shù)
一個(gè)CEF3應(yīng)用程序會(huì)運(yùn)行多個(gè)進(jìn)程,這些進(jìn)程能夠使用同一個(gè)執(zhí)行器或者為子進(jìn)程定制的、單獨(dú)的執(zhí)行器。進(jìn)程的執(zhí)行從入口函數(shù)開(kāi)始。當(dāng)執(zhí)行子進(jìn)程時(shí),CEF將使用命令行參數(shù)指定配置信息,這些命令行參數(shù)必須通過(guò)CefMainArgs結(jié)構(gòu)體傳入到CefExecuteProcess函數(shù)。
在Windows平臺(tái)下,它接收wWinMain函數(shù)傳入的參數(shù):實(shí)例句柄(HINSTANCE),這個(gè)實(shí)例能夠通過(guò)函數(shù)GetModuleHandle(NULL)獲取。

CefMainArgs main_args(hInstance);

5、單一執(zhí)行體
當(dāng)以單一執(zhí)行體運(yùn)行時(shí),根據(jù)不同的進(jìn)程類型,入口函數(shù)有差異。
在這里插入圖片描述

int main(int argc, char* argv[]) {
      // Structure for passing command-line arguments.
      // The definition of this structure is platform-specific.
      CefMainArgs main_args(argc, argv);
    
      // Optional implementation of the CefApp interface.
      CefRefPtr<SimpleApp> app(new SimpleApp);
    
      // Execute the sub-process logic, if any. This will either return immediately for the browser
      // process or block until the sub-process should exit.
      int exit_code = CefExecuteProcess(main_args, app.get());
      if (exit_code >= 0) {
        // The sub-process terminated, exit now.
        return exit_code;
      }
    
      // Populate this structure to customize CEF behavior.
      CefSettings settings;
    
      // Initialize CEF in the main process.
      CefInitialize(main_args, settings, app.get());
    
      // Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
      CefRunMessageLoop();
    
      // Shut down CEF.
      CefShutdown();
    
      return 0;
    }

6、分離子進(jìn)程執(zhí)行體
當(dāng)使用獨(dú)立的子進(jìn)程執(zhí)行體時(shí),你需要2個(gè)分開(kāi)的可執(zhí)行工程和2個(gè)分開(kāi)的入口函數(shù)。
在我們的項(xiàng)目中我采用的就是分離子進(jìn)程執(zhí)行體。主程序的入口就是游戲的入口,而子程序入口就是新建的一個(gè)工程(zt2asHelp)的入口。

主程序的入口函數(shù):
在這里插入圖片描述

// Program entry-point function.
// 程序入口函數(shù)
int main(int argc, char* argv[]) {
  // Structure for passing command-line arguments.
  // The definition of this structure is platform-specific.
  // 傳遞命令行參數(shù)的結(jié)構(gòu)體。
  // 這個(gè)結(jié)構(gòu)體的定義與平臺(tái)相關(guān)。
  CefMainArgs main_args(argc, argv);

  // Optional implementation of the CefApp interface.
  // 可選擇性地實(shí)現(xiàn)CefApp接口
  CefRefPtr<SimpleApp> app(new SimpleApp);

  // Populate this structure to customize CEF behavior.
  // 填充這個(gè)結(jié)構(gòu)體,用于定制CEF的行為。
  CefSettings settings;

  // Specify the path for the sub-process executable.
  // 指定子進(jìn)程的執(zhí)行路徑
  CefString(&settings.browser_subprocess_path).FromASCII(“/path/to/subprocess”);

  // Initialize CEF in the main process.
  // 在主進(jìn)程中初始化CEF 
  CefInitialize(main_args, settings, app.get());

  // Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
  // 執(zhí)行消息循環(huán),此時(shí)會(huì)堵塞,直到CefQuitMessageLoop()函數(shù)被調(diào)用。
  CefRunMessageLoop();

  // Shut down CEF.
  // 關(guān)閉CEF
  CefShutdown();

  return 0;
}

子進(jìn)程程序的入口函數(shù):
在這里插入圖片描述

// Program entry-point function.
    // 程序入口函數(shù)
    int main(int argc, char* argv[]) {
      // Structure for passing command-line arguments.
      // The definition of this structure is platform-specific.
      // 傳遞命令行參數(shù)的結(jié)構(gòu)體。
      // 這個(gè)結(jié)構(gòu)體的定義與平臺(tái)相關(guān)。
      CefMainArgs main_args(argc, argv);
    
      // Optional implementation of the CefApp interface.
      // 可選擇性地實(shí)現(xiàn)CefApp接口
      CefRefPtr<SimpleApp> app(new SimpleApp);
    
      // Execute the sub-process logic. This will block until the sub-process should exit.
      // 執(zhí)行子進(jìn)程邏輯,此時(shí)會(huì)堵塞直到子進(jìn)程退出。
      return CefExecuteProcess(main_args, app.get());
    }

7、CefSettings
結(jié)構(gòu)體允許定義全局的CEF配置,經(jīng)常用到的配置項(xiàng)如下:
single_process設(shè)置為true時(shí),Browser和Renderer使用一個(gè)進(jìn)程。此項(xiàng)也可以通過(guò)命令行參數(shù)“single-process”配置。(single_process == true 時(shí)cef3不穩(wěn)定,調(diào)試可以用下)
browser_subprocess_path設(shè)置用于啟動(dòng)子進(jìn)程單獨(dú)執(zhí)行器的路徑。

8、CefApp
CefApp接口提供了不同進(jìn)程的可定制回調(diào)函數(shù)。畢竟重要的回調(diào)函數(shù)如下:
GetBrowserProcessHandler 返回定制Browser進(jìn)程的Handler,該Handler包括了諸如OnContextInitialized的回調(diào)。
GetRenderProcessHandler 返回定制Render進(jìn)程的Handler,該Handler包含了JavaScript相關(guān)的一些回調(diào)以及消息處理的回調(diào)。

9、CefClient
CefClient提供訪問(wèn)Browser實(shí)例的回調(diào)接口。一個(gè)CefClient實(shí)現(xiàn)可以在任意數(shù)量的Browser進(jìn)程中共享。以下為幾個(gè)重要的回調(diào):
比如處理Browser的生命周期,右鍵菜單,對(duì)話框,通知顯示, 拖曳事件,焦點(diǎn)事件,鍵盤(pán)事件等等。如果沒(méi)有對(duì)某個(gè)特定的處理接口進(jìn)行實(shí)現(xiàn)會(huì)造成什么影響,請(qǐng)查看cef_client.h文件中相關(guān)說(shuō)明。
OnProcessMessageReceived在Browser收到Render進(jìn)程的消息時(shí)被調(diào)用。

到這你差不多就知道cef3是怎么一回事了。我們可以上代碼了

五、上代碼

先說(shuō)兩點(diǎn):
1、在游戲窗口之前初始化Cef
2、在關(guān)閉游戲之前先關(guān)閉Cef
在游戲工程中添加兩個(gè)函數(shù)分別進(jìn)行初始化和關(guān)閉Cef

// 初始化函數(shù):
        void GameAppation::InitializeCef()
        {
         // 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);  // hInstance是游戲窗口的實(shí)例句柄(HINSTANCE)
        
          // Parse command-line arguments.
          CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
          command_line->InitFromString(::GetCommandLineW());
        
          // Create a ClientApp of the correct type.
          CefRefPtr<CefApp> app;
          // The command-line flag won't be specified for the browser process.
          if (!command_line->HasSwitch("type"))
          {
        	  app = new SimpleApp();
          }
          else
          {
        	  const std::string& processType = command_line->GetSwitchValue("type");
        	  if (processType == "renderer")
        	  {
        		  app = new SimpleApp();
        	  }
        	  else
        	  {
        		 // app = new SimpleOtherApp();
        	  }
          }
        
          // 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
        
        // 在這設(shè)置用于啟動(dòng)子進(jìn)程單獨(dú)執(zhí)行器的路徑
          WCHAR subProcessPath[260] = { 0 };
          GetModuleFileNameW(hInstance, subProcessPath, 260);
          *(wcsrchr(subProcessPath, L'\\') + 1) = L'\0';
          LPCWSTR SUB_PROCESS_NAME = L"zt2asHelp.exe";
          wcsncat_s(subProcessPath, 260, SUB_PROCESS_NAME, wcslen(SUB_PROCESS_NAME));
          cef_string_from_wide(subProcessPath, 260, &settings.browser_subprocess_path);
        
          // 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);
        }
    
  //  關(guān)閉函數(shù)
        void GameAppation::UnInitializeCef()
        {
           CefShutdown();
        }

將cefsimple工程中的simple_app.cc、simple_handle.cc、simle_handle_win.cc及simple_app.h、simple_handle.h添加進(jìn)游戲工程中。并添加兩個(gè)文件simple_v8_handler.cc、simple_v8_handler.h。在分離子進(jìn)程執(zhí)行體的工程里新加一個(gè)文件zt2asHelp.cpp,并將上面幾個(gè)文件添加進(jìn)工程。
先上代碼,后面再分析。
修改文件代碼:

simple_app.h

// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

#include "include/cef_app.h"

// Implement application-level callbacks for the browser process.
class SimpleApp : public CefApp,
                  public CefBrowserProcessHandler,
				  public CefRenderProcessHandler {
 public:
  SimpleApp();

  // CefApp methods:
  virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() OVERRIDE { return this; }
  virtual CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() OVERRIDE { return this; }
  virtual void OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) OVERRIDE;
  virtual void OnWebKitInitialized() OVERRIDE;

  // CefBrowserProcessHandler methods:
  virtual void OnContextInitialized() OVERRIDE;

  virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
	  CefProcessId source_process,
	  CefRefPtr<CefProcessMessage> message) OVERRIDE;

 private:

 CefRefPtr<SimpleV8JsHandler> m_MyJShander = nullptr;

  // Include the default reference counting implementation.
  IMPLEMENT_REFCOUNTING(SimpleApp);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

simple_app.cc

// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#include "simple_app.h"
#include "Simple_V8_JS_Handler.h"
#include <tchar.h>

#include <string>

#include "simple_handler.h"
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/wrapper/cef_helpers.h"

SimpleApp::SimpleApp() {
}

void SimpleApp::OnContextInitialized() {
	// 這塊的內(nèi)容由游戲里去創(chuàng)建 
}

void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, CefRefPtr<CefV8Context> context) 
{
	// The var type can accept all object or variable
	CefRefPtr<CefV8Value> window = context->GetGlobal();

	if (!m_MyJShander)
	{
		m_MyJShander = new SimpleV8JsHandler();
	}

	CefRefPtr<CefV8Value> strValue = CefV8Value::CreateString("");
	window->SetValue("token", strValue, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> strValue1 = CefV8Value::CreateUInt(0);
	window->SetValue("accid", strValue, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> myFunc = CefV8Value::CreateFunction(_T("youjin"), m_MyJShander);
	window->SetValue(_T("youjin"), myFunc, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> myFunc2 = CefV8Value::CreateFunction(_T("mimang"), m_MyJShander);
	window->SetValue(_T("mimang"), myFunc2, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> myFunc3 = CefV8Value::CreateFunction(_T("dejin"), m_MyJShander);
	window->SetValue(_T("dejin"), myFunc3, V8_PROPERTY_ATTRIBUTE_NONE);

	CefRefPtr<CefV8Value> myFunc4 = CefV8Value::CreateFunction(_T("mubiao"), m_MyJShander);
	window->SetValue(_T("mubiao"), myFunc4, V8_PROPERTY_ATTRIBUTE_NONE);
}

void SimpleApp::OnWebKitInitialized()
{
	std::string extensionCode =
		"var g_value=\"global value here\";"
		"var zt2as;"
		"if (!zt2as)"
		"  zt2as = {};"
		"(function() {"
		"  zt2as.youjin = function() {"
		"    native function youjin();"
		"    return youjin();"
		"  };"
		"})();"
		"(function() {"
		"  zt2as.mimang = function() {"
		"    native function mimang();"
		"    return mimang();"
		"  };"
		"})();"
		"(function() {"
		"  zt2as.dejin = function() {"
		"    native function dejin();"
		"    return dejin();"
		"  };"
		"})();"
		"(function() {"
		"  zt2as.mubiao = function() {"
		"    native function mubiao();"
		"    return mubiao();"
		"  };"
		"})();";

	// 聲明本地函數(shù) native function hehe();" 如果有參數(shù)列表需要寫(xiě)具體的類型,而不能寫(xiě)var類型!與本地聲明一直
	// 調(diào)用本地函數(shù)    return hehe();"

	// Create an instance of my CefV8Handler object.
	CefRefPtr<CefV8Handler> handler = new SimpleV8JsHandler();

	// Register the extension.
	CefRegisterExtension("v8/mycode", extensionCode, handler);
}

bool SimpleApp::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
	CefProcessId source_process,
	CefRefPtr<CefProcessMessage> message) 
{
	const std::string& messageName = message->GetName();

	if (messageName == "func_back")
	{
		CefRefPtr<CefListValue> argList = message->GetArgumentList();
		std::string token_ = argList->GetString(0);
		uint32_t accid_ = argList->GetInt(1);

		CefRefPtr<CefV8Value> strValue = CefV8Value::CreateString(token_);
		browser->GetMainFrame()->GetV8Context()->GetGlobal()->SetValue("token", strValue, V8_PROPERTY_ATTRIBUTE_NONE);

		CefRefPtr<CefV8Value> strValue1 = CefV8Value::CreateUInt(accid_);
		browser->GetMainFrame()->GetV8Context()->GetGlobal()->SetValue("accid", strValue1, V8_PROPERTY_ATTRIBUTE_NONE);

		return true;
	}

	return false;
}

simple_handle.h

// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

#include "include/cef_client.h"

#include <list>

class SimpleHandler : public CefClient,
                      public CefDisplayHandler,
                      public CefLifeSpanHandler,
                      public CefLoadHandler,
					  public CefContextMenuHandler,
					  public CefJSDialogHandler
{
 public:
  SimpleHandler();
  ~SimpleHandler();

  enum MY_MENU 
  {
	  MENU_ID_USER_OPENLINK = MENU_ID_USER_FIRST + 200,
	  MENU_ID_USER_SHOWDEVTOOLS,
  };

  // Provide access to the single global instance of this object.
  static SimpleHandler* GetInstance();

  // CefClient methods:
  virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE {
    return this;
  }
  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE {
    return this;
  }
  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE {
    return this;
  }

  virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() OVERRIDE {
	  return this;
  }

  virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() OVERRIDE {
	  return this;
  }


  // CefDisplayHandler methods:
  virtual void OnTitleChange(CefRefPtr<CefBrowser> browser,
                             const CefString& title) OVERRIDE;

  // CefLifeSpanHandler methods:
  virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
  virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;

  // CefLoadHandler methods:
  virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
                           CefRefPtr<CefFrame> frame,
                           ErrorCode errorCode,
                           const CefString& errorText,
                           const CefString& failedUrl) OVERRIDE;

  virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
	  CefRefPtr<CefFrame> frame,
	  CefRefPtr<CefContextMenuParams> params,
	  CefRefPtr<CefMenuModel> model) OVERRIDE;

  virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
	  CefRefPtr<CefFrame> frame,
	  CefRefPtr<CefContextMenuParams> params,
	  int command_id,
	  EventFlags event_flags) OVERRIDE;
	
  virtual bool OnJSDialog(CefRefPtr<CefBrowser> browser,
	  const CefString& origin_url,
	  const CefString& accept_lang,
	  JSDialogType dialog_type,
	  const CefString& message_text,
	  const CefString& default_prompt_text,
	  CefRefPtr<CefJSDialogCallback> callback,
	  bool& suppress_message) OVERRIDE;

  // Request that all existing browser windows close.
  void CloseAllBrowsers(bool force_close);

  bool IsClosing() const { return is_closing_; }

  virtual bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
	  CefProcessId source_process,
	  CefRefPtr<CefProcessMessage> message) OVERRIDE;

  void SetToken(std::string str) { token_ = str; }

  std::string GetToken() { return token_; }

  void SetAccid(uint32_t accid) { accid_ = accid; }

  uint32_t GetAccid() { return accid_; }

  CefRefPtr<CefBrowser> GetBrowser() { return browser_; }

  void ShowDevelopTools(CefRefPtr<CefBrowser> browser);

  HWND GetGameHwnd() { return gameHwnd; }

  void SetGameHwnd(HWND h) { gameHwnd = h; }

 private:
  // List of existing browser windows. Only accessed on the CEF UI thread.
  typedef std::list<CefRefPtr<CefBrowser> > BrowserList;
  BrowserList browser_list_;

  bool is_closing_;

  CefRefPtr<CefBrowser> browser_ = nullptr;

  std::string token_ = "";

  uint32_t accid_ = 0;

  HWND  gameHwnd = NULL;

  // Include the default reference counting implementation.
  IMPLEMENT_REFCOUNTING(SimpleHandler);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

simple_handle.cc

// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#include "simple_handler.h"

#include <sstream>
#include <string>

#include "include/base/cef_bind.h"
#include "include/cef_app.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"

#include <fstream>
#include <Windows.h>
#include <tchar.h>

namespace {

SimpleHandler* g_instance = NULL;

}  // namespace

SimpleHandler::SimpleHandler()
    : is_closing_(false) {
  DCHECK(!g_instance);
  g_instance = this;
}

SimpleHandler::~SimpleHandler() {
  g_instance = NULL;
}

// static
SimpleHandler* SimpleHandler::GetInstance() {
  return g_instance;
}

void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Add to the list of existing browsers.
  browser_list_.push_back(browser);

  browser_ = browser;
}

bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Closing the main window requires special handling. See the DoClose()
  // documentation in the CEF header for a detailed destription of this
  // process.
  if (browser_list_.size() == 1) {
    // Set a flag to indicate that the window close should be allowed.
    is_closing_ = true;
  }

  // Allow the close. For windowed browsers this will result in the OS close
  // event being sent.
  return false;
}

void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Remove from the list of existing browsers.
  BrowserList::iterator bit = browser_list_.begin();
  for (; bit != browser_list_.end(); ++bit) {
    if ((*bit)->IsSame(browser)) {
      browser_list_.erase(bit);
      break;
    }
  }

  if (browser_list_.empty()) {
    // All browser windows have closed. Quit the application message loop.
    CefQuitMessageLoop();
  }
}

void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
                                CefRefPtr<CefFrame> frame,
                                ErrorCode errorCode,
                                const CefString& errorText,
                                const CefString& failedUrl) {
  CEF_REQUIRE_UI_THREAD();

  // Don't display an error for downloaded files.
  if (errorCode == ERR_ABORTED)
    return;

  // Display a load error message.
  std::stringstream ss;
  ss << "<html><body bgcolor=\"white\">"
        "<h2>Failed to load URL " << std::string(failedUrl) <<
        " with error " << std::string(errorText) << " (" << errorCode <<
        ").</h2></body></html>";
  frame->LoadString(ss.str(), failedUrl);
}

void SimpleHandler::CloseAllBrowsers(bool force_close) {
  if (!CefCurrentlyOn(TID_UI)) {
    // Execute on the UI thread.
    CefPostTask(TID_UI,
        base::Bind(&SimpleHandler::CloseAllBrowsers, this, force_close));
    return;
  }

  if (browser_list_.empty())
    return;

  BrowserList::const_iterator it = browser_list_.begin();
  for (; it != browser_list_.end(); ++it)
    (*it)->GetHost()->CloseBrowser(force_close);
}

bool SimpleHandler::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
	CefProcessId source_process,
	CefRefPtr<CefProcessMessage> message)
{
	const std::string& messageName = message->GetName();
	if (messageName == "msg_from_render")
	{
		CefRefPtr<CefListValue> argList = message->GetArgumentList();
		CefRefPtr<CefProcessMessage> msg_back = CefProcessMessage::Create("func_back");
		msg_back->GetArgumentList()->SetString(0, token_);
		msg_back->GetArgumentList()->SetInt(1, accid_);
		browser->SendProcessMessage(PID_RENDERER, msg_back);

		return true;
	}
	return false;
}

void SimpleHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
	CefRefPtr<CefFrame> frame,
	CefRefPtr<CefContextMenuParams> params,
	CefRefPtr<CefMenuModel> model) 
{
	//model->Clear(); // 禁止菜單只需這一句

	model->Remove(MENU_ID_PRINT);
	model->Remove(MENU_ID_VIEW_SOURCE);

	if ((params->GetTypeFlags() & (CM_TYPEFLAG_PAGE | CM_TYPEFLAG_FRAME)) != 0)
	{
		if (model->GetCount() > 0) 
		{
			model->RemoveAt(2);
			model->Remove(MENU_ID_BACK);
			model->Remove(MENU_ID_FORWARD);

			model->AddItem(MENU_ID_USER_SHOWDEVTOOLS,"developerTools");
		}
	}
}

bool SimpleHandler::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
	CefRefPtr<CefFrame> frame,
	CefRefPtr<CefContextMenuParams> params,
	int command_id,
	EventFlags event_flags) 
{
	switch (command_id)
	{
	case MENU_ID_USER_SHOWDEVTOOLS: 
		{
			ShowDevelopTools(browser);
			return true;
		}
		break;
	}

	return  false;
}

void SimpleHandler::ShowDevelopTools(CefRefPtr<CefBrowser> browser)
{
	CefWindowInfo windowinfo;
	CefBrowserSettings settings;

	windowinfo.SetAsPopup(NULL, "DevTools");

	browser->GetHost()->ShowDevTools(windowinfo, this, settings, CefPoint());
}

bool SimpleHandler::OnJSDialog(CefRefPtr<CefBrowser> browser,
	const CefString& origin_url,
	const CefString& accept_lang,
	JSDialogType dialog_type,
	const CefString& message_text,
	const CefString& default_prompt_text,
	CefRefPtr<CefJSDialogCallback> callback,
	bool& suppress_message) 
{
	CEF_REQUIRE_UI_THREAD();

	std::wstring msg = message_text.ToWString();

	if (dialog_type == JSDIALOGTYPE_ALERT)
	{
		::MessageBoxW(gameHwnd, (LPCWSTR)msg.c_str(), L"提示", MB_OK | MB_TOPMOST);

		suppress_message = true;

		return false;
	}
	else if(dialog_type == JSDIALOGTYPE_CONFIRM)
	{
		int a = ::MessageBoxW(gameHwnd, (LPCWSTR)msg.c_str(), L"提示", MB_OKCANCEL | MB_TOPMOST);
		if (a == IDOK)
		{
			callback->Continue(true, "");
		}
		else
		{
			callback->Continue(false, "");
		}

		suppress_message = false;
		return true;
	}

	return false;
}

simple_v8_handler.h

#pragma once

#include <include/cef_v8.h>
class SimpleV8JsHandler :
	public CefV8Handler
{
public:
	SimpleV8JsHandler(void);
	virtual ~SimpleV8JsHandler(void);

public:
	virtual bool Execute(const CefString& name,
		CefRefPtr<CefV8Value> object,
		const CefV8ValueList& arguments,
		CefRefPtr<CefV8Value>& retval,
		CefString& exception) OVERRIDE;

	IMPLEMENT_REFCOUNTING(SimpleV8JsHandler);
};

simple_v8_handler.cc

#include "Simple_V8_JS_Handler.h"
#include <tchar.h>

SimpleV8JsHandler::SimpleV8JsHandler(void)
{
}

SimpleV8JsHandler::~SimpleV8JsHandler(void)
{
}

bool SimpleV8JsHandler::Execute(const CefString& func_name,
	CefRefPtr<CefV8Value> object,
	const CefV8ValueList& arguments,
	CefRefPtr<CefV8Value>& retval,
	CefString& exception)
{
	if (func_name == _T("youjin"))
	{
		if (arguments.size() == 0)
		{
				CefRefPtr<CefFrame> frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
				retval = frame->GetV8Context()->GetGlobal()->GetValue("token");
		}
		return true;
	}
	else if (func_name == _T("mimang"))
	{
		if (arguments.size() == 0)
		{
				CefRefPtr<CefFrame> frame = CefV8Context::GetCurrentContext()->GetBrowser()->GetMainFrame();
				retval = frame->GetV8Context()->GetGlobal()->GetValue("accid");
		}
		return true;
	}
	else if (func_name == _T("dejin"))
	{
		retval = CefV8Value::CreateString("To Hit My Stride");
		return true;
	}
	else if (func_name == _T("mubiao"))
	{
		CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("msg_from_render");
		CefRefPtr<CefListValue> args = msg->GetArgumentList();
		args->SetString(0, name);
		CefV8Context::GetCurrentContext()->GetBrowser()->SendProcessMessage(PID_BROWSER, msg);
	}
	
	return false;
}

zh2asHelp.cpp

#include "include/cef_app.h"
#include "include/internal/cef_win.h"
#include <windows.h>
#include "simple_app.h"
#include "include/cef_base.h"

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{
	CefMainArgs main_args(hInstance);

	CefRefPtr<SimpleApp> app(new SimpleApp);

	return CefExecuteProcess(main_args, app.get(), NULL);
}

至此,幾乎所有的核心代碼都準(zhǔn)備好了,除了在游戲里創(chuàng)建我們的瀏覽器。

六、坑

因?yàn)槲覀働C游戲只創(chuàng)建一個(gè)windows窗口,游戲里面的dialog都是模擬出來(lái)的,并非windows窗口。要把cef直接顯示游戲里,并且是可以拖動(dòng)位置,我的做法是在游戲里面創(chuàng)建一個(gè)新的windows子窗口,同時(shí)在游戲里創(chuàng)建一個(gè)游戲窗口,把windows子窗口的位置綁定到新建的游戲窗口上,當(dāng)拖動(dòng)游戲窗口時(shí),同時(shí)也移動(dòng)windows子窗口,然后再把cef的瀏覽器內(nèi)容顯示在windows子窗口上。
這樣就簡(jiǎn)單實(shí)現(xiàn)了在游戲里顯示網(wǎng)頁(yè)內(nèi)容。但是我們要求網(wǎng)頁(yè)是有賬號(hào)登陸的,一個(gè)游戲id是綁定一個(gè)賬號(hào)的,你在游戲內(nèi)打開(kāi)網(wǎng)頁(yè),是要求自動(dòng)登陸的,也就是要把游戲里的token值和accid值發(fā)送給網(wǎng)頁(yè)。而token值和accid值是在我們游戲exe進(jìn)程里的,要知道cef是多進(jìn)程架構(gòu)的,Browser被定義為主進(jìn)程,負(fù)責(zé)窗口管理,界面繪制和網(wǎng)絡(luò)交互。Blink的渲染和Js的執(zhí)行被放在一個(gè)獨(dú)立的Render 進(jìn)程中。我們的問(wèn)題就變成了把數(shù)據(jù)從游戲進(jìn)程傳給Render進(jìn)程,Render進(jìn)程里的js調(diào)用我們給的數(shù)據(jù)。見(jiàn)下圖
在這里插入圖片描述
關(guān)于Cef的C++和Js通信的文章很多,附上一篇英文版:https:///chromiumembedded/cef/wiki/JavaScriptIntegration.md;
對(duì)應(yīng)的中文翻譯版:https://www.cnblogs.com/guolixiucai/p/4943748.html

七、還有很多坑

像游戲里的焦點(diǎn)問(wèn)題,鍵盤(pán)事件問(wèn)題等等。
附上JS測(cè)試代碼

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset="utf-8" />
		<script type="text/javascript" >
			function youjin()
			{
				alert(zt2as.getToken());
			}
			function mimang()
			{
				alert(zt2as.getAccId());
			}
			function dejin()
			{
				alert(zt2as.getDevice());
			}
			function mubiao()
			{
				alert(zt2as.initData());
				alert(zt2as.getAccId());
				alert(zt2as.getToken());
			}
		</script>
	</head>
	<body style="width:100%;height:100%;background-color:white;">
		<p>回望2018 奔向2019</p>
		<div >
			<button onclick="youjin();">getToken</button>
			<button onclick="mimang();">getAccId</button>
			<button onclick="dejin();">getDevice</button>
			<button onclick="mubiao();">initData</button>
		</div>
	</body>
</html>

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章