如何利用CEF3创建一个简单的应用程序 (Windows Platform)
说明
这篇文章主要讲述如何利用CEF3来创建一个简单的应用程序,引用的是1535及以上版本中包含的 Cefsimple 项目例子。如果想知道关于CEF3更多的使用方法,可以去访问 GeneralUsage.
开始
首先,根据自身所使用的开发平台,可以去 这里 下载对应的发布版本。针对这个教程,我们需要下载1750或者更新的版本。当前支持的平台有Windows, Linux和Mac OS X。每一个版本都包含了当在特定平台上编译特定版本CEF3时所需要的所有文件和资源。您可以通过包含在里边的 REDME.txt 文件或者在Wiki上 GeneralUsage 中的 Getting Started,了解每个发布版本的具体内容和细节。
编译发布版本中的项目
以CEF发布版本为基础开发的应用程序可以使用标准的平台编译工具进行编译执行。包括 Windows 平台下的 Visual Studio, Mac OS X 平台下的 Xcode, 以及 Linux 平台下的 gcc/make。针对平台的不同,项目的编译过程也有些许的不同和要求。
Windows
Windows 平台下编译 Cefsimple 步骤:
1. 用对应的 Visual Studio 版本打开项目解决方案。举个例子,如果你安装的是 Visual Studio 2010, 那么,打开的就是 cesimple2010.sln。
2. 如果你下载的是 x64版本,请确保你选择的是 x64的开发平台。
3. 开始编译。
4. 如果编译通过,那么,在当前解决方案的目录下,将出现“out/Debug”(或者 “out/Release”)文件夹。
5. 执行文件夹下 cefsimple.exe, 确保能正确运行。
加载一个自定义URL
cefsimple项目中默认加载的URL是 google.com,当然,你也可以用自定义的 URL 去替代它,最方便的就是通过命令行搞定。
# Load the local file “c:\example\example.html” cefsimple.exe --url=file://c:/example/example.html
除了命令行的方法,也可以通过直接修改在 cefsimple/simple.cpp 文件中的代码,达到你的目的。
# Load the local file “c:\example\example.html” … if (url.empty()) url = file://c:/example/example.html;
应用程序组成
所有的 CEF 应用程序都有一下主要组成部分:
1. CEF 的动态链接库 。(在 Windows 平台下就是 libcef.dll)
2. 支持库。(ICU, FFMPEG等)
3. 资源。(html/js/css, strings等)
4. 客户端执行文件。(本教程中就是 cefsimple.exe.)
要点(必看)
1. CEF 使用的是多进程。应用程序主进程是浏览器进程,而其他子进程是由 renderer, plugins, GPU等创建。
2. 在 Windows 和 Linux 平台下的执行文件可以被主进程和子进程使用。
3. CEF 中所有进程都可以是多线程的。CEF提供了许多功能和接口在不同的线程中传递任务。
4. 一些回调方法和函数只能在特定的进程或者线程中使用。在你第一次使用新的回调方法或者函数之前,请确保你已经阅读了 API 头文件中源码,看使用要求。
流程分析
cefsimple 应用程序首先初始化CEF,然后创建了一个简单的弹出浏览器窗口。当关闭了所有的浏览器窗口,应用程序就会结束。程序执行流程如下:
1. 系统执行入口点函数(main or wWinMain),并创建浏览器进程。
2. 入口点函数:
1. 创建能够处理进程级别的回调方法的 SimpleApp 实例。
2. 初始化 CEF,进入 CEF 消息循环。
3. 初始化 CEF 之后,调用 SimpleApp::OnContextInitialized() 。这个方法中:
1. 创建单例的 SimpleHandler 。
2. 由 CefBrowserHost::CreateBrowsersync() 方法创建一个浏览器窗口。
4. 所有的浏览器共享 SimpleHandler 实例, 此实例能定制浏览器行为、处理浏览器相关回调方法(life span, loading state, title display等)。
5. 当一个浏览器窗口关闭的时候,调用 SimpleHandler::OnBeforeClose() 。当所有的浏览器窗口全部关闭时,OnBeforeClose() 函数就会执行跳出 CEF 消息循环的行为,退出应用程序。
入口点函数
程序的运行开始于浏览器进程中的入口点函数。这个函数会初始化 CEF 以及所有跟操作系统有关的对象。
Windows
#include <windows.h> #include "cefsimple/simple_app.h" #include "include/cef_sandbox_win.h" // Set to 0 to disable sandbox support. #define CEF_ENABLE_SANDBOX 1 #if CEF_ENABLE_SANDBOX // The cef_sandbox.lib static library is currently built with VS2010. It may not // link successfully with other VS versions. #pragma comment(lib, "cef_sandbox.lib") #endif // Entry point function for all processes. int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); void* sandbox_info = NULL; #if CEF_ENABLE_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); // SimpleApp implements application-level callbacks. It will create the first // browser instance in OnContextInitialized() after CEF has initialized. CefRefPtr<SimpleApp> app(new SimpleApp); // 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, app.get(), 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 !CEF_ENABLE_SANDBOX settings.no_sandbox = true; #endif // 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; }
SimpleApp
SimpleApp 负责处理进程级别的回调方法。它会曝露出一些在多进程中共享或者被特定进程使用的接口和方法。CefBrowserProcessHandler 接口,在浏览器进程中调用。还有一个被分离出 CefBrowserProcessHandler 接口(例子项目没有展示)只会在渲染进程中被调用。由于 CefBrowserProcessHandler 不光实现了 CefApp, 同时还有 CefBrowserProcessHandler,所以它的返回值必须是[this]。
// 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); }; // simple_app.cpp #include "cefsimple/simple_app.h" #include <string> #include "cefsimple/simple_handler.h" #include "cefsimple/util.h" #include "include/cef_browser.h" #include "include/cef_command_line.h" SimpleApp::SimpleApp() { } void SimpleApp::OnContextInitialized() { REQUIRE_UI_THREAD(); // Information used when creating the native window. CefWindowInfo window_info; #if defined(OS_WIN) // On Windows we need to specify certain flags that will be passed to // CreateWindowEx(). window_info.SetAsPopup(NULL, "cefsimple"); #endif // SimpleHandler implements browser-level callbacks. CefRefPtr<SimpleHandler> handler(new SimpleHandler()); // Specify CEF browser settings here. CefBrowserSettings browser_settings; std::string url; // Check if a "--url=" value was provided via the command-line. If so, use // that instead of the default URL. CefRefPtr<CefCommandLine> command_line = CefCommandLine::GetGlobalCommandLine(); url = command_line->GetSwitchValue("url"); if (url.empty()) url = "http://www.google.com"; // Create the first browser window. CefBrowserHost::CreateBrowserSync(window_info, handler.get(), url, browser_settings, NULL); }
SimpleHandler
SimpleHandler 负责处理浏览器级别的回调方法。这些回调方法会在浏览器进程中执行。在这个项目中,针对所有的浏览器使用相同的 CefClient 实例,但是如果你愿意,可以在自己的应用程序中使用不同的 CefClient实例的。
// 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 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); private: // List of existing browser windows. Only accessed on the CEF UI thread. typedef std::list<CefRefPtr<CefBrowser> > BrowserList; BrowserList browser_list_; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(SimpleHandler); }; // simple_handler.cpp #include "cefsimple/simple_handler.h" #include <sstream> #include <string> #include "cefsimple/util.h" #include "include/cef_app.h" #include "include/cef_runnable.h" namespace { SimpleHandler* g_instance = NULL; } // namespace SimpleHandler::SimpleHandler() { ASSERT(!g_instance); g_instance = this; } SimpleHandler::~SimpleHandler() { g_instance = NULL; } // static SimpleHandler* SimpleHandler::GetInstance() { return g_instance; } void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) { REQUIRE_UI_THREAD(); // Add to the list of existing browsers. browser_list_.push_back(browser); } void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) { 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) { 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, NewCefRunnableMethod(this, &SimpleHandler::CloseAllBrowsers, 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); } // simple_handler_win.cpp -- Windows-specific code. #include "cefsimple/simple_handler.h" #include <string> #include <windows.h> #include "cefsimple/util.h" #include "include/cef_browser.h" void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) { REQUIRE_UI_THREAD(); CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle(); SetWindowText(hwnd, std::wstring(title).c_str()); } // simple_handler_gtk.cc -- Linux-specific code. #include "cefsimple/simple_handler.h" #include <gtk/gtk.h> #include <string> #include "cefsimple/util.h" #include "include/cef_browser.h" void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) { REQUIRE_UI_THREAD(); GtkWidget* window = gtk_widget_get_ancestor( GTK_WIDGET(browser->GetHost()->GetWindowHandle()), GTK_TYPE_WINDOW); std::string titleStr(title); gtk_window_set_title(GTK_WINDOW(window), titleStr.c_str()); } // simple_handler_mac.mm -- OS X-specific code. #include "cefsimple/simple_handler.h" #import <Cocoa/Cocoa.h> #include "cefsimple/util.h" #include "include/cef_browser.h" void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) { REQUIRE_UI_THREAD(); NSView* view = (NSView*)browser->GetHost()->GetWindowHandle(); NSWindow* window = [view window]; std::string titleStr(title); NSString* str = [NSString stringWithUTF8String:titleStr.c_str()]; [window setTitle:str]; }
编译步骤(Windows)
1. 编译 libcef_dll_wrapper 静态库。
2. 编译/链接项目。
1. 源文件:cefsimple_win.cpp, simple_app.cpp, simple_handler.cpp, simple_handler_win.cpp.
2. 链接库:comctl32.lib, shlwapi.lib, rcprt4.lib, libcef_dll_wrapper.lib, libcef.lib, cef_sandbox.lib. (cef_sandbox.lib 是 sandbox 支持,当前内部仅支持 VS2010, 如果使用其他VS版本,可以将 cefsimple_win.cpp 中 的 CEF_ENABLE_SANDBOX 设置为 0)
3. 将有关的库和资源复制到输出目录。
4. 将 Debug/Release 文件夹下文件复制到输出目录。
输出目录的结构应该如下:
Application/ cefsimple.exe <= cefsimple application executable libcef.dll <= main CEF library icudt.dll <= ICU unicode support library ffmpegsumo.dll <= HTML5 audio/video support library libEGL.dll, libGLESv2.dll, … <= accelerated compositing support libraries cef.pak, devtools_resources.pak <= non-localized resources and strings locales/ en-US.pak, … <= locale-specific resources and strings