1. 程式人生 > >duilib + cef簡單瀏覽器的demo

duilib + cef簡單瀏覽器的demo

參考連線:

https://bitbucket.org/chromiumembedded/cef/wiki/Home

自己寫的demo地址:

http://download.csdn.net/detail/shuaixingrumo/9556308

關於編譯duilib的庫和cef庫的步驟不詳細說了, 網上有很多這樣的文章

首先我們要先新增倆個類:

一個是SimpleApp, 用來初始化cef context環境的類, 一個是 SimpleHandler類, 和cef互動主要靠它

simple_handler.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:
  SimpleHandler();
  ~SimpleHandler();

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

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

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

  bool IsClosing() const { return is_closing_; }

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

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

	 CefRefPtr<CefBrowser> m_browser;

  bool is_closing_;

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

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_


simple_handler.cpp:

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

namespace {

SimpleHandler* g_instance = NULL;

}  // namespace

SimpleHandler::SimpleHandler()
    : is_closing_(false), m_browser(NULL) {
  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);*/
  m_browser = browser;
}

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

  if(m_browser)
	  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();

  if(m_browser->IsSame(browser))
	  m_browser = NULL;
}

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);
}

SimpleHandler類繼承了CefLifeSpanHandler, 用於管理CefBrowser的生存週期

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"

class SimpleApp : public CefApp,
                  public CefBrowserProcessHandler {
 public:
  SimpleApp();

  // CefApp methods:
  virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
      OVERRIDE { return this; }

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

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

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

simple_app.cpp:
// 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 <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() {
  
}

注意 simple_app的標頭檔案要放在前面, 否則會出現編譯錯誤

添加了這倆個類之後, 我們接下來要做的是初始化cef, 在main函式中新增如下程式碼:

void* sandbox_info = NULL;
	CefMainArgs main_args(hInstance);
	CefRefPtr<SimpleApp> app(new SimpleApp);
	CefSettings settings;
	settings.no_sandbox = true;
	settings.multi_threaded_message_loop = true;
	CefInitialize(main_args, settings, app.get(), sandbox_info);

這裡需要注意的是, CefSettings物件, 我們設定了multi_threaded_message_loop=true, 這樣設定的目的是,使cef 的browser ui執行緒和我們程式的執行緒分離, 我們可以使用duilib的訊息迴圈函式CPaintManagerUI::MessageLoop(), 而不必呼叫cef的CefRunMessageLoop()。

接下來新增我們的duilib程式碼:

CPaintManagerUI::SetInstance(hInstance);
	CMainWnd wnd;
	wnd.Create(NULL, L"", UI_WNDSTYLE_FRAME | WS_THICKFRAME, WS_EX_ACCEPTFILES);
	wnd.ShowWindow();
	wnd.CenterWindow();
	CPaintManagerUI::MessageLoop();
	CefShutdown();


退出訊息迴圈一定要呼叫CefShutdown()來關閉cef

在duilib的視窗類的initWindow()中新增如下程式碼來嵌入cef視窗:

CefWindowInfo info;
	info.SetAsChild(m_hWnd, rt);

	CefBrowserSettings settings;
	CefBrowserHost::CreateBrowser(info, m_handler.get(), L"http://www.baidu.com", settings, NULL);

rt是一個RECT物件, 表示你放置cef視窗的區域

這樣, 把cef3嵌入到duilib視窗中的工作就基本完成了, 下面就可以設計我們自己需要的功能了:

例如新增一個URL輸入視窗, 並跳轉到對應的頁面, 我們可以新增如下程式碼:

std::wstring strUrl = m_pURL->GetText();
m_handler->GetBrowser()->GetMainFrame()->LoadURL(strUrl);

m_handler是一個SimpleHandler物件, m_pURL是一個duilib的編輯框物件

後退到前一個頁面:

m_handler->GetBrowser()->GoBack();

調整視窗大小:
HWND hwnd = ::FindWindowEx(m_hWnd, NULL, L"CefBrowserWindow", NULL);
		::MoveWindow(hwnd, 3, 100, LOWORD(lParam)-6, HIWORD(lParam)-103, TRUE);


載入拖拽的本地html檔案:

WCHAR wcFile[MAX_PATH] = {0};
		UINT count = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL, 0);
		if(count)
		{
			DragQueryFile((HDROP)wParam, 0, wcFile, MAX_PATH);
			wstring str = wcFile;
			m_handler->GetBrowser()->GetMainFrame()->LoadURL(str);
			m_pURL->SetText(wcFile);
		}

接一下來, 我們會講解如何在cef中實現 js 和 c++的互動操作。

注:demo程式碼中, 需要在OnFinalMessage中處理下退出邏輯:

例如改為如下程式碼:

while(m_handler->GetBrowser())
{
Sleep(10);
}
::PostQuitMessage(0);

先判斷下, cef是否關閉了, 再退出訊息迴圈, 以防退出時候崩潰